Imagine that you have this generic pseudo-code:
template<typename Iterable>
void f(Iterable&& iterable)
{
...
}
We want to handle rvalue and lvalue references to iterable objects1, and the idea is that the function handles the container performing operations element by element.
It is plausible that we want to forward the reference specification of the container to the elements. In other words, if iterable
is an rvalue reference, the function will have to move the elements from the container.
Using C++17, I would do
auto [begin, end] = [&] {
if constexpr(std::is_lvalue_reference_v<Iterable>)
return std::array{std::begin(iterable),
std::end(iterable)};
else
return std::array{
std::make_move_iterator(std::begin(iterable)),
std::make_move_iterator(std::end(iterable))};
}();
std::for_each(begin, end, [&](auto&& element)
{
...
});
Obviously, this is not the best code to maintain2, error prone and probably not so easy to optimize for the compiler.
My question is: it could be possible, for future C++ standards, to introduce the concept of forwarding range-based loops? It would be nice if this
for(auto&& el : std::move(iterable))
{
...
}
could handle el as rvalue reference. In this way, this would be possible:
template<typename Iterable>
void f(Iterable&& iterable)
{
for(auto&& el : std::forward<Iterable>(iterable))
{
/*
* el is forwarded as lvalue reference if Iterable is lvalue reference,
* as rvalue reference if Iterable is rvalue reference
*/
external_fun(std::forward<decltype(el)>(el));
}
}
I am concerned about code-breaking changes, but at the same time I am not able to think about situations in which passing a rvalue reference as argument of a range based loop is expected to work without moving objects.
As suggested, I tried to write down how I would change the 6.5.4 section of the standard. The draft can be read at this address.
Do you think that it would be possible to introduce this feature without introducing serious issues?
1Checked with C++20 concepts or static_asserts
2And it's quite worse without C++17