c++iteratorvalue-categoriesfor-range

Why doesn't range-based for loop match rvalue-reference modifier of this?


#include <vector>

class MyContainer {
public:
    std::vector<int> data;

    // begin() only defined for rvalues
    auto begin() && { return data.begin(); }
    auto end() && { return data.end(); }
};

MyContainer container;
container.data = {1, 2, 3};

// why not working ? compile error;
for (int x : MyContainer(container)) {
      std::cout << x << " ";
}

clang c++ 20 complain:

'this' argument to member function 'begin' is an lvalue, but function has rvalue ref-qualifier;

but why ? MyContainer(container) is rvalue, it SHOULD match begin() &&, right ?
I know using MyContainer(container).begin() is ok, but how to use it in for range loop directly?


update: why i want those to work ?

for iter = xxx.begin(); if xxx is rvalue, *iter return rvalue too; so, for

for (auto &&e : Container()) {
    x = e;         // call move directly;
}

for (auto &&e : container) {
    x = e;         // call copy directly;
}

it's very convenient for implements many move aware of algorithm; but seems standard not aware of it for now;
hops it update in future!

solution for current c++ standard (20)

for (auto itb = move(container).begin(), ite = move(container).end(); itb != ite; ++ itb) {
    x = *itb;            // ok, call move;
}

for (auto &&e : container) {
    x = e;              // ok, call copy as it is;
}

Solution

  • As you can see in cppreference.com, a ranged-based for loop is equivalent to a regular for loop, scoped with some variables defined, and with specific init-statement, condition etc.

    There are some changes depending on the C++ version, but for C++20 that you use it is:

    {
        init-statement
        auto&& /* range */ = range-initializer ;
        auto /* begin */ = /* begin-expr */;
        auto /* end */ = /* end-expr */;
        for ( ; /* begin */ != /* end */; ++/* begin */)
        {
            item-declaration = */* begin */;
            statement
        }
    }
    

    It is a bit different in earlier C++ versions, but anyway since C++11 there is always an equivalent for a variable:

    auto&& /* range */ = range-initializer ;
    

    This variable is the one used for calling begin() and end().
    But it has a name and is therefore an lvalue (even if you supply an rvalue as your original range-like object).

    This is the reason the compiler rejects calling the rvalue ref (&&) qualified begin() and end() with it.

    In order to use a ranged-based for loop you should remove the && qualification from them.