简体   繁体   English

std :: enable_if或SFINAE用于迭代器或指针

[英]std::enable_if or SFINAE for iterator or pointer

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 ). 我想为MyClass编写一个带参数的构造函数,我希望只有在参数是pointeriterator (具有iterator_traits东西)时才能编译它。 How to achieve this ? 怎么做到这一点?

Regrettably, there is no standard way to detect whether a class models Iterator . 遗憾的是,没有标准的方法来检测一个类是否为Iterator模型。 The simplest check would be that *it and ++it are both syntactically valid; 最简单的检查是*it++it在语法上都是有效的; you can do this using standard SFINAE techniques: 你可以使用标准的SFINAE技术做到这一点:

template<typename T,
    typename = decltype(*std::declval<T&>(), void(), ++std::declval<T&>(), void())>
    MyClass(T);

Considering the Iterator requirements from 24.2.2:2: 考虑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
     )> {};

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). 尝试使用iterator_traits的问题在于它是为所有类型定义的模板,并且其实例化将在非SFINAE上下文中失败(回想一下SFINAE仅适用于直接替换失败)。 libstdc++ has a conforming extension whereby instantiating iterator_traits on non-iterator types will produce an empty type; libstdc ++具有一致的扩展 ,通过在非迭代器类型上实例化iterator_traits将产生一个空类型; you can do a similar trick by checking for the existence of iterator_category on the type: 你可以通过检查类型上是否存在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);

This will however not work for types that do not themselves expose iterator_category but have been adapted by a separate iterator_traits specialisation; 但是,这对于那些本身不公开iterator_category但已经通过单独的iterator_traits进行调整的类型不起作用 ; 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). 在这种情况下,简单的SFINAE方法更有意义(并且您可以在构造函数中实例化iterator_traits以确认类型是类迭代器)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM