4
#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;
}

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

9 Comments

i don't think c++ specification says it always a lvalue; even a variable is lvalue, but it can forward to rvalue;
Not sure I follow. It is specified that the ranged-based loop is equivalent to a normal for loop with some variables. The variables are lvalues, so you cannot call your begin(),end() with it.
Spec might have added some std::forward to make OP's code works...
Just speculation, but they already missed some temporary lifetime extension in C++11, move was young at that stage.
I would not expect to be able to call end() on a temporary that had already been consumed by a begin() and left in a valid-but-unspecified state.
|
1

8.6.4.1 [stmt.ranged]

The range-based for statement

for ( init-statement for-range-declaration : for-range-initializer ) statement

is equivalent to

{
  init-statement

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

The above quote from the C++ standard suggests that your loop will look like the following snippet:

{
    auto&& range = MyContainer(container);
    auto begin = range.begin();
    auto end = range.end();
    for(; begin != end; ++begin) {
        int x = *begin;
        std::cout << x << " ";
    }
}

As you can see, begin() and end() are both called on range, which is a named l-value at this point.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.