简体   繁体   中英

C++20 Concepts: Element iterable concept

I am trying to create a concept ElementIterable which can determine the type is nested ranges or not. For example, the elements in std::vector<int> is not iterable, but the elements ( std::vector<int> ) in std::vector<std::vector<int>> is iterable. The idea about using std::iterator_traits<T> comes up in my mind and the experimental code is as following. However, this ElementIterable concept doesn't work as the expected behavior. Is there any idea to fix this ElementIterable concept?

template<typename T>
concept ElementIterable = requires(typename std::iterator_traits<T>::value_type x)                        //  requires-expression
{
    x.begin();          // must have `x.begin()`
    x.end();            // and `x.end()`
};

The usage of this ElementIterable is here.

template<typename T> requires ElementIterable<T>
void Foo(T input);

template<typename T> requires ElementIterable<T>
void Foo(T input)
{
    std::cout << "Element iterable" << std::endl;
}

template<typename T>
void Foo(T input);

template<typename T>
void Foo(T input)
{
    std::cout << "Element not iterable" << std::endl;
}

The usage of the function Foo .

int number = 1;
    
std::vector<decltype(number)> vector1;
vector1.push_back(number);
Foo(vector1);           //  Element not iterable

std::vector<decltype(vector1)> vector2;
vector2.push_back(vector1);
Foo(vector2);           //  Element not iterable
                        //      but expected behaviour is: Element iterable

All suggestions are welcome.

If you want to ask if a type is a range which itself contains a range, that's simply applying the std::range type twice:

template<typename T>
concept nested_range = std::ranges::range<T> && std::ranges::range<std::ranges::range_value_t<T>>

range_value_t extracts the value_type from the iterator type of the range. Here's a live example .

Well, the problem is std::iterator_traits<T> . The argument for iterator_traits is supposed to be an iterator type. Meanwhile, you want the concept to be applicable unto containers. Since std::iterator_traits is designed to be SFINAE friendly, and it's unlikely a container will satisfy enough of the legacy iterator concept, it's more than likely std::iterator_traits<T> has no members when you check your concept. That leads to the concept not being satisfied.

Why not rely on the concepts in the ranges header? It has a handy utility to obtain the value type of a type that satisfies the range concept

#include <ranges>

template<typename T>
concept ElementIterable = requires(std::ranges::range_value_t<T> x)
{
    x.begin();          // must have `x.begin()`
    x.end();            // and `x.end()`
};

Or, slightly more robust and without reinventing standard traits

template<typename T>
concept ElementIterable = std::ranges::range<std::ranges::range_value_t<T>>;

C++20 concepts can be as dumb (as in, text replacing) as you need them to be. In your case, simple template duck typing should do the job by checking for what you need to exist, ie iterator functions in the results of your type's iterator functions.

With that in mind, you can try something like this:

template<typename T>
concept ElementIterable = requires(T x)
{
    x.begin()->begin();        
    x.end()->end();            
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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