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

2024-01-10

我想写一个构造函数MyClass需要一个参数,我希望只有当参数是一个时才编译pointer or an iterator(有的东西iterator_traits)。如何实现这一目标?


遗憾的是,没有标准的方法来检测一个类是否模型Iterator。最简单的检查是*it and ++it两者在语法上都是有效的;您可以使用标准 SFINAE 技术来做到这一点:

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

考虑到Iterator24.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++ 有一个一致扩展 http://gcc.gnu.org/onlinedocs/gcc-4.7.1/libstdc++/api/a01471_source.html#l00143由此实例化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);

然而这将not work对于本身不公开的类型iterator_category但已被单独改编iterator_traits专业化;在这种情况下,简单的 SFINAE 方法更有意义(并且您可以实例化iterator_traits在构造函数中确认该类型是类似迭代器的)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

std::enable_if 或 SFINAE 用于迭代器或指针 的相关文章

随机推荐