Match iterable types (arrays and classes with begin()/end())

前端 未结 1 945
鱼传尺愫
鱼传尺愫 2021-01-13 06:21

I wrote type traits like classes that can be used test if a given type is \"iterable\". This is true for arrays (for T[N], not for T[]) and for cla

1条回答
  •  囚心锁ツ
    2021-01-13 06:40

    First, some boilerplate to do easy argument dependent lookup of begin in a context where std::begin is visible:

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

    This is required to reasonably emulate how range-based for(:) loops find their begin/end iterators. By packaging it up like this, we reduce boilerplate below.

    Next, some C++1y style utility aliases:

    templatestruct sink {using type=void;};
    templateusing sink_t=typename sink::type;
    templateusing enable_if_t=typename std::enable_if::type;
    

    sink_t takes any type, and throws it away replacing it with void.

    enable_if_t removes annoying typename spam below.

    In an industrial strength library, we'd put this in details, and have a 1-type-argument version that dispatches to it. But I don't care:

    template struct is_iterator:std::false_type{};
    template<> struct is_iterator:std::false_type{};
    template<> struct is_iterator:std::false_type{};
    template<> struct is_iterator:std::false_type{};
    template<> struct is_iterator:std::false_type{};
    templatestruct is_iterator::value_type >
    >:std::true_type{};
    

    is_iterator doesn't do heavy auditing of the iterator_traits of I. But it is enough.

    template
    using begin_t=decltype(adl_begin(std::declval()));
    template
    using end_t=decltype(adl_end(std::declval()));
    

    These two type aliases make the stuff below less annoying.

    Again, in industrial strength libraries, put 2-arg-with-void into details:

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

    Note the commented out line in the enable_if_t above. I left that out to allow asymmetric iteration to work, where the end is a type that has a different operator== overload. Such is being considered for C++17: it allows really, really efficient algorithms on null-terminated strings (for example).

    Finally, the final output:

    templateusing iterator_t=enable_if_t::type, begin_t>;
    

    which evaluates to the iterator of the iterable range R iff it has one.

    There are cases where this won't work, but they are pathological.

    live example

    0 讨论(0)
提交回复
热议问题