std :: enable_if或SFINAE用于迭代器或指针 [英] std::enable_if or SFINAE for iterator or pointer
问题描述
我想为 MyClass
写一个构造函数,它接受一个参数,只有当参数是一个指针时,我才需要编译。 code>或
迭代器
(有 iterator_traits
)。如何实现这个?
I would like to write a constructor for MyClass
that take an argument and I want this to compile only if the argument is a pointer
or an iterator
(something having iterator_traits
). How to achieve this ?
推荐答案
遗憾的是,没有标准的方法来检测类模型 Iterator
。最简单的检查是 * it
和 ++ it
都是语法有效的;您可以使用标准的SFINAE技术:
Regrettably, there is no standard way to detect whether a class models Iterator
. The simplest check would be that *it
and ++it
are both syntactically valid; you can do this using standard SFINAE techniques:
template<typename T,
typename = decltype(*std::declval<T&>(), void(), ++std::declval<T&>(), void())>
MyClass(T);
考虑 Iterator
要求从24.2.2 :2:
Considering the Iterator
requirements from 24.2.2:2:
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 ++具有符合的扩展,借此实例化 iterator_traits
在非迭代器类型将产生一个空类型;您可以通过检查类型上是否存在 iterator_category
来执行类似的操作:
The problem with trying to use iterator_traits
is that it is a template defined for all types, and its instantiation will fail in a non-SFINAE context (recall that SFINAE only applies for direct substitution failure). libstdc++ has a conforming extension whereby instantiating iterator_traits
on non-iterator types will produce an empty type; you can do a similar trick by checking for the existence of iterator_category
on the type:
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
,以确认类型是类迭代器)。
This will however not work for types that do not themselves expose iterator_category
but have been adapted by a separate iterator_traits
specialisation; in that case the simple SFINAE method makes more sense (and you can instantiate iterator_traits
within the constructor to confirm that the type is iterator-like).
这篇关于std :: enable_if或SFINAE用于迭代器或指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!