There are a number of different ways, that make a type/class usable in a ranged for loop. An overview is for example given on cppreference:
range-expression
is evaluated to determine the sequence or range to iterate. Each element of the sequence, in turn, is dereferenced and is used to initialize the variable with the type and name given in range-declaration.
begin_expr
andend_expr
are defined as follows:
- If
range-expression
is an expression of array type, thenbegin_expr
is__range
andend_expr
is (__range
+__bound
), where__bound
is the number of elements in the array (if the array has unknown size or is of an incomplete type, the program is ill-formed)- If
range-expression
is an expression of a class typeC
that has both a member namedbegin
and a member namedend
(regardless of the type or accessibility of such member), thenbegin_expr
is__range.begin()
andend_expr
is__range.end()
- Otherwise,
begin_expr
isbegin(__range)
andend_expr
isend(__range)
, which are found via argument-dependent lookup (non-ADL lookup is not performed).
If I want to use a ranged for loop say in a function template, I want to constrain the type to be usable in a ranged for loop, to trigger a compiler error with a nice "constraint not satisfied" message. Consider the following example:
template<typename T>
requires requires (T arg) {
// what to write here ??
}
void f(T arg) {
for ( auto e : arg ) {
}
}
Obviously for a generic function I want to support all of the above listed ways, that a type can use to make itself ranged for compatible.
This brings me to my questions:
- Is there a better way than manually combining all the different ways into a custom concept, is there some standard library concept that I can use for that? In the concepts library, there is no such thing. And if there is really no such thing, is there a reason for that?
- If there is no library/builtin concept for that, how am I supposed to implement such
thing. What really puzzles me, is how to test for members
begin
andend
regardless of the type or accessibility of such member (second bullet in the qouted list). A requires clause that tests for example for the existence of abegin()
member fails ifbegin()
is private, but the ranged for loop would be able to use the type regardless of that.
Note I am aware of the following two questions:
- What concept allows a container to be usable in a range-based for loop?
- How to make my custom type to work with "range-based for loops"?
but neither of them really answers my question.
std::begin
/std::end
works for arrays, and will call thebegin/end
members if they exists. So that should cover all 3 cases already. – Afterclapbegin
/end
. When the type has privatebegin
andend
then the range based for loop will attempt to use them and not fall back tostd::begin
andstd::end
. Hence the range based for loop is not able to use such type: godbolt.org/z/3dMPaPb57 – Bunkobegin
/end
members will cause the ranged for loop to not compile. – Chrisoula