3

I wrote the following program and expected that the rvalue gotten from std::move() would be destroyed right after it's used in the function call:

struct A
{
    A(){ }
    A(const A&){ std::cout << "A&" << std::endl; }
    ~A(){ std::cout << "~A()" << std::endl; }
    A operator=(const A&){ std::cout << "operator=" << std::endl; return A();}
};

void foo(const A&&){ std::cout << "foo()" << std::endl; }

int main(){
    const A& a = A();
    foo(std::move(a)); //after evaluation the full-expression 
                       //rvalue should have been destroyed
    std::cout << "before ending the program" << std::endl;
}

But it was not. The following output was produced instead:

foo()
before ending the program
~A()

DEMO

As said in the answer

rvalues denote temporary objects which are destroyed at the next semicolon

What did I get wrong?

Community
  • 1
  • 1
user3663882
  • 6,957
  • 10
  • 51
  • 92

5 Answers5

6

std::move does not make a into a temporary value. Rather it creates an rvalue reference to a, which is used in function foo. In this case std::move is not doing anything for you.

The point of std::move is that you can indicate that a move constructor should be used instead of a copy constructor, or that a function being called is free to modify the object in a destructive way. It doesn't automatically cause your object to be destructed.

So what std::move does here is that if it wanted to, the function foo could modify a in a destructive way (since it takes an rvalue reference as its argument). But a is still an lvalue. Only the reference is an rvalue.

There's a great reference here that explains rvalue references in detail, perhaps that will clear a few things up.

rlbond
  • 65,341
  • 56
  • 178
  • 228
  • But the fact that an rvalue, not an rvalue reference to lvalue is being detroyed after a full-expression it contains has finished is correct? – user3663882 Jul 06 '15 at 19:39
  • Yes. For example, try this: `foo(A())`. The temporary `A` object will be destroyed after the semicolon. – rlbond Jul 06 '15 at 20:32
  • `std::move(a)` cannot trigger the move constructor in this specific example, because `a` is a const lvalue, hence `std::move(a)` is a const rvalue, but rvalue references do not bind to const rvalues. – fredoverflow Jul 06 '15 at 21:27
  • rvalue references are in the language: http://www.codesynthesis.com/~boris/blog/2012/07/24/const-rvalue-references/ – rlbond Jul 06 '15 at 21:31
2

Remember: std::move doesn't move the object. std::move is a simple cast that takes an lvalue and makes it look like an rvalue

foo, by taking an argument by rvalue reference, says that the input object will be modified, but left in a valid state. Nothing here about destroying the object.

In the end, a remains an lvalue, no matter how much you try to cast it.

KABoissonneault
  • 2,359
  • 18
  • 17
2

std::move(a) does not alter a to become an rvalue.

Instead, it creates an rvalue reference to a.


Edit:

Note that, with your line

const A& a = A();

you rely on a the special case of local const references prolonging the lives of temporaries (see e.g. http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/). This feature predates C++11. As temporaries are basically rvalues, I understand now where your confusion comes from.

Note that by prolonging the life of the temporary (by means of assigning it to the local const reference), your object referenced by a cannot be classified as "object(s) which are destroyed at the next semicolon". Rather, it lives as long as the reference.

May someone else locate http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/ in the C++11 standard. Same for your sentence "rvalues denote temporary objects which are destroyed at the next semicolon".

Ludwig Schulze
  • 2,155
  • 1
  • 17
  • 36
1

The rvalue you get from std::move is an rvalue reference, and references don't have a destructor. You can't get to that reference anymore. So why don't you think it has been destroyed?

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
0

You didn't get anything wrong; rather, it is the committee that made a mistake in giving std::move that name. It's just a cast, making it easier to "select" move constructors and move assignment operators that actually perform moves. In this case you have neither, so the std::move does literally nothing. You're seeing the original object being destroyed when it goes out of scope as usual.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055