匹配可迭代类型(使用begin()/ end()的数组和类)

时间:2014-08-09 23:14:48

标签: c++ templates typetraits c++14

我写了类似于类的特性,如果给定的类型是"可迭代的话,可以用它来测试#34;。对于数组(对于T[N]而不对于T[])以及具有beginend方法的类返回看起来像迭代器的类,情况都是如此。我想知道它是否可以比我做得更简洁/更简单?

特别是impl命名空间中的内容看起来有点迂回/ hacky。这一切对我来说都有点难看。有关使用此示例并且可以使用g ++和clang ++编译的示例,请参阅:https://gist.github.com/panzi/869728c9879dcd4fffa8

template<typename T>
struct is_iterator {
private:
    template<typename I> static constexpr auto test(void*)
        -> decltype(
            *std::declval<const I>(),
            std::declval<const I>() == std::declval<const I>(),
            std::declval<const I>() != std::declval<const I>(),
            ++ (*std::declval<I*>()),
            (*std::declval<I*>()) ++,
            std::true_type()) { return std::true_type(); }

    template<typename I> static constexpr std::false_type test(...) { return std::false_type(); }

public:
    static constexpr const bool value = std::is_same<decltype(test<T>(0)), std::true_type>::value;
};

namespace impl {
    // implementation details

    template<typename T>
    struct has_iterable_methods {
    private:

        template<typename C> static constexpr auto test(void*)
            -> decltype(
                std::declval<C>().begin(),
                std::declval<C>().end(),
                std::true_type()) { return std::true_type(); }

        template<typename C> static constexpr std::false_type test(...) { return std::false_type(); }

    public:
        static constexpr const bool value = std::is_same<decltype(test<T>(0)), std::true_type>::value;
    };

    template<typename T, bool HasIterableMethods>
    struct returns_iterators : public std::false_type {};

    template<typename T>
    struct returns_iterators<T, true> {
        typedef decltype(std::declval<T>().begin()) begin_type;
        typedef decltype(std::declval<T>().end())   end_type;

        static constexpr const bool value =
            std::is_same<begin_type, end_type>::value &&
            is_iterator<begin_type>::value;
    };
}

template<typename T>
struct is_iterable : public std::integral_constant<
    bool,
    impl::returns_iterators<
        typename std::remove_const<T>::type,
        impl::has_iterable_methods<typename std::remove_const<T>::type>::value>::value> {};

template<typename T, std::size_t N>
struct is_iterable<T[N]> : public std::true_type {};

template<typename T>
struct is_iterable<T*> : public std::false_type {};

1 个答案:

答案 0 :(得分:6)

首先,在begin可见的上下文中,对std::begin进行简单的参数依赖查找的一些样板:

#include <utility>
#include <iterator>
namespace adl_details {
  using std::begin; using std::end;
  template<class R>
  decltype(begin(std::declval<R>())) adl_begin(R&&r){
    return begin(std::forward<R>(r));
  }
  template<class R>
  decltype(end(std::declval<R>())) adl_end(R&&r){
    return end(std::forward<R>(r));
  }
}
using adl_details::adl_begin;
using adl_details::adl_end;

这需要合理地模拟基于范围的for(:)循环如何找到它们的开始/结束迭代器。通过这样打包,我们减少了下面的样板。

接下来,一些C ++ 1y样式实用程序别名:

template<class>struct sink {using type=void;};
template<class X>using sink_t=typename sink<X>::type;
template<bool b, class T=void>using enable_if_t=typename std::enable_if<b,T>::type;

sink_t采用任何类型,并将其删除,将其替换为void

enable_if_t删除了下面恼人的typename垃圾邮件。

在一个工业强度库中,我们将它放在detail中,并且有一个分派给它的1类型参数版本。但我不在乎:

template<class I,class=void> struct is_iterator:std::false_type{};
template<> struct is_iterator<void*,void>:std::false_type{};
template<> struct is_iterator<void const*,void>:std::false_type{};
template<> struct is_iterator<void volatile*,void>:std::false_type{};
template<> struct is_iterator<void const volatile*,void>:std::false_type{};
template<class I>struct is_iterator<I,
  sink_t< typename std::iterator_traits<I>::value_type >
>:std::true_type{};

is_iterator不会对iterator_traits I进行大量审核。但这已经足够了。

template<class R>
using begin_t=decltype(adl_begin(std::declval<R&>()));
template<class R>
using end_t=decltype(adl_end(std::declval<R&>()));

这两种类型的别名使下面的内容不那么烦人。

再次,在工业强度库中,将2-arg-with - void放入details

template<class R,class=void> struct has_iterator:std::false_type{};
template<class R>
struct has_iterator<
  R,
  enable_if_t<
    is_iterator<begin_t<R>>::value
    && is_iterator<end_t<R>>::value
    // && std::is_same<begin_t<R>,end_t<R>>::value
  >
>:std::true_type{};

请注意上面enable_if_t中注释掉的行。我把它留下来允许非对称迭代工作,其中end是一种具有不同operator==重载的类型。对于C ++ 17正在考虑这样的问题:它允许在以null结尾的字符串上实现真正有效的算法(例如)。

最后,最终输出:

template<class R>using iterator_t=enable_if_t<has_iterator<R>::type, begin_t<R>>;

如果它有一个,那么它将计算可迭代范围R的迭代器。

有些情况下这不起作用,但它们是病态的。

live example