7

I can't seem to get around this specific problem for some time now. For example if I have the following code:

void foo(std::vector<int>::iterator &it) { // ... } int main(){ std::vector<int> v{1,2,3}; foo(v.begin()); } 

I would get compile error:

initial value of reference to non-const must be an lvalue. 

And my guess would be that I get the error because a.begin() returns a rvalue.

If so how is it possible that the following expression works:

v.begin()=v.begin()++; 

if v.begin() is a rvalue?

6
  • It is legal to call non-const member functions on temporaries of class type. As in v.begin().operator=(v.begin().operator++()). It is sometimes useful, but it does lead to odd code compiling when those member functions are overloaded operators. Commented Dec 27, 2020 at 0:19
  • If they did not specify that iterators should prohibit assignment on rvalues in standard, this looks like a defect to me Commented Dec 27, 2020 at 0:26
  • 2
    This is related stackoverflow.com/questions/5890382/… Commented Dec 27, 2020 at 0:27
  • foo should take the iterator by value. That’s how iterators are intended to be used. It’s very unusual to traffic in references to iterators. Commented Dec 27, 2020 at 0:40
  • @alterigel OP's concern why assignment to rvalue compiles. It does compile on gcc in C++14 mode at least, so it is not related to pesky MS extention Commented Dec 27, 2020 at 0:43

1 Answer 1

9

The reason is historical. In the initial days of the language, there was simply no way for user code to express that a type's copy-assignment operator should only work on l-values. This was only true for user-defined types of course; for in-built types assignment to an r-value has always been prohibited.

int{} = 42; // error 

Consequently, for all types in the standard library, copy-assignment just "works" on r-values. I don't believe this ever does anything useful, so it's almost certainly a bug if you write this, but it does compile.

std::string{} = "hello"s; // ok, oops 

The same is true for the iterator type returned from v.begin().

From C++11, the ability to express this was added in the language. So now one can write a more sensible type like this:

struct S { S& operator=(S const &) && = delete; // ... etc }; 

and now assignment to r-values is prohibited.

S{} = S{}; // error, as it should be 

One could argue that all standard library types should be updated to do the sensible thing. This might require a fair amount of rewording, as well as break existing code, so this might not be changed.

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.