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.