我想为 MyClass
编写一个带有参数的构造函数,我希望仅当参数是 pointer
或 iterator
(具有 iterator_traits
的东西)时才编译它。如何实现这一目标?
最佳答案
遗憾的是,没有标准的方法来检测一个类是否建模 Iterator
。最简单的检查是 *it
和 ++it
在语法上都有效;你可以使用标准的 SFINAE 技术来做到这一点:
template<typename T,
typename = decltype(*std::declval<T&>(), void(), ++std::declval<T&>(), void())>
MyClass(T);
考虑 24.2.2:2 的
Iterator
要求:template<typename T> typename std::enable_if<
!std::is_void<decltype(*std::declval<T &>())>::value
&& std::is_same<decltype(++std::declval<T &>()),
typename std::add_lvalue_reference<T>::type>::value,
std::true_type>::type has_iterator_requirements_helper(int);
template<typename T> std::false_type has_iterator_requirements_helper(...);
template<typename T> struct has_iterator_requirements:
decltype(has_iterator_requirements_helper<T>(0)) {};
template<typename, bool> struct is_iterator_check: std::false_type {};
template<typename T> struct is_iterator_check<T, true>: std::true_type {
typedef typename std::iterator_traits<T>::difference_type difference_type;
typedef typename std::iterator_traits<T>::value_type value_type;
typedef typename std::iterator_traits<T>::iterator_category iterator_category;
typedef typename std::iterator_traits<T>::reference reference;
typedef typename std::iterator_traits<T>::pointer pointer;
static_assert(std::is_same<decltype(*std::declval<T &>()), reference>::value
|| std::is_void<reference>::value, "*r must be of type reference");
};
template<typename T> struct is_iterator: is_iterator_check<T,
(std::is_pointer<T>::value
&& !std::is_void<typename std::remove_pointer<T>::type>::value
&& !std::is_function<typename std::remove_pointer<T>::type>::value
) || (std::is_copy_constructible<T>::value
&& std::is_copy_assignable<T>::value
&& std::is_nothrow_destructible<T>::value
// TODO: check lvalues are swappable
&& has_iterator_requirements<T>::value
)> {};
尝试使用
iterator_traits
的问题在于它是为所有类型定义的模板,并且它的实例化将在非 SFINAE 上下文中失败(回想一下 SFINAE 仅适用于直接替换失败)。 libstdc++ 有一个 conforming extension,借此在非迭代器类型上实例化 iterator_traits
将产生一个空类型;您可以通过检查类型上是否存在 iterator_category
来执行类似的技巧:template<typename T> std::true_type has_iterator_category_helper(
T::iterator_category *);
template<typename T> std::false_type has_iterator_category_helper(...);
template<typename T> struct has_iterator_category<T>:
decltype(has_iterator_category_helper<T>(0)) { };
template<typename T> struct is_iterator: std::integral_constant<bool,
std::is_pointer<T>::value || has_iterator_category<T>::value> {};
template<typename T, typename = std::enable_if<is_iterator<T>::value>>
MyClass(T);
然而,这不适用于本身不公开
iterator_category
但已由单独的 iterator_traits
特化改编的类型;在这种情况下,简单的 SFINAE 方法更有意义(并且您可以在构造函数中实例化 iterator_traits
以确认该类型类似于迭代器)。关于c++ - 迭代器或指针的 std::enable_if 或 SFINAE,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12161109/