When looking at what range-based for loops actually do here: https://en.cppreference.com/w/cpp/language/range-for#Explanation, I see that the begin_expr and end_expr are taken by value, i.e. using copy constructor:
auto __begin = begin_expr ;
auto __end = end_expr ;
Is there a reason the begin/end are taken like that, or is it an oversight? Wouldn't it be better to either move them:
auto __begin = std::move(begin_expr) ;
auto __end = std::move(end_expr) ;
or to take them by forwarding ref:
auto && __begin = begin_expr ;
auto && __end = end_expr ;
CodePudding user response:
Is there a reason the begin/end are taken like that, or is it an oversight? Wouldn't it be better to either move them:
In general no, because such specification could potentially prohibit copy elision.
Why in range for loop do begin/end need to be copyable?
The initialisation that you quote doesn't generally necessitate the iterators to be copyable, except in an unusual case where begin / end return an lvalue reference. I'm not sure if anyone cares about such case, given that would violate concept requirements.
CodePudding user response:
In the case of a user-defined type, the expectation is those expressions are prvalues.
It will either be __range.begin() or (adl) begin(__range), which are both function calls. If that is returned by value, there is no object to copy, as it is an unmaterialised temporary at that point, thus there is no point in std::moveing it. If that is returned by reference, std::moveing it will mutate __range to move-initialise __begin, which is a surprising behaviour.
In the case of an array, begin-expr is a lvalue, but that is trivial to copy. end-expr is a prvalue already.
