Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I use assignment operator on begin() even if it is an rvalue?

Tags:

c++

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?

like image 825
Kerim Nurikic Avatar asked Dec 27 '20 00:12

Kerim Nurikic


People also ask

How to use rvalue reference?

If the function argument is an rvalue, the compiler deduces the argument to be an rvalue reference. For example, assume you pass an rvalue reference to an object of type X to a template function that takes type T&& as its parameter. Template argument deduction deduces T to be X , so the parameter has type X&& .

Why move semantics c++?

Move semantics allows you to avoid unnecessary copies when working with temporary objects that are about to evaporate, and whose resources can safely be taken from that temporary object and used by another.

What is a semantic move?

Move semantics is about transferring resources rather than copying them when nobody needs the source value anymore. In C++03, objects are often copied, only to be destroyed or assigned-over before any code uses the value again.


1 Answers

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.

like image 126
cigien Avatar answered Sep 20 '22 02:09

cigien