2
int c = 2;
int d = std::move(c);
std::cout << "c is: " << c << std::endl;
std::cout << "d is: " << d << std::endl;

this code output:

c is: 2

d is: 2

I thought that after move(c) to d, c will be empty, why does it still have 2 as its value ? Can you anyone please help me explain this ? Thank you.

LIU Qingyuan
  • 524
  • 5
  • 18
MrMiami
  • 41
  • 3
  • 2
    Does this answer your question? [copy vs std::move for ints](https://stackoverflow.com/questions/27888873/copy-vs-stdmove-for-ints) – cigien May 18 '20 at 09:52
  • 3
    How should an _empty integer_ look like? Having value 0? Why 0 then? – Daniel Langr May 18 '20 at 10:06
  • In short, moving is a fancy way of copying. It's different from copying only for types that *own* some resources (files, dynamically allocated memory, etc). – HolyBlackCat May 18 '20 at 10:19
  • BTW, the purpose of move semantics is to make some operations _faster_. If a compiler would need to zero-out the moved-from integer, this would actually end up being slower than copying. – Daniel Langr May 18 '20 at 10:29

3 Answers3

6

I thought that after move(c) to d, c will be empty,

Your expectation was mis informed.

why does it still have 2 as its value ?

Fundamental types do not have move constructors. You have simply made a copy. Copying does not modify the source object.


For class types, it would not be safe to assume what the move constructor does exactly, and specifically what state the source object is left in. It is not necessarily guaranteed to be "empty". See the documentation of the class for what it does. If there is no documentation, or documentation doesn't give any guarantees, then you cannot assume anything about the state of the source object.

eerorika
  • 232,697
  • 12
  • 197
  • 326
4

std::move doesn't move anything! (contrary to it's name). It is exactly equivalent to a static_cast to an rvalue reference type.

That it, it is just a cast to rvalue- more specifically to an xvalue, as opposed to a prvalue. And it is also true that having a cast named move sometimes confuses people. However the intent of this naming is not to confuse, but rather to make your code more readable.

Using the xvalue, we can trigger the right overload and hence, we can use std::swap in such overloads to take the ownership of another object (but aren't required).

For example, a move constructor of a linked list might copy the pointer to the head of the list and store nullptr in the argument instead of allocating and copying individual nodes.

why does it still have 2 as its value

As mentioned std::move doesn't move and the real job of swapping/moving the resources is being performed by the overloads like move constructor and move assignment. std::move tasks is just to cast so that compiler can call the right overload (for example, move constructor in favor of copy constructor) and the actual moving of resources has to be defined by the software developer in their respective overloads. Since, fundamental types like int doesn't have any move constructor, the statement int c = std::move(a); merely copies the value of a to c.

Try this:

#include <iostream>
#include <utility>


void hello(int& a)
{
    std::cout << "LVALUE" << std::endl;
}

void hello(int&& a)
{
    std::cout << "RVALUE" << std::endl;
}

int main(void)
{
    int a = 8;
    hello(a);
    hello(std::move(a));
    return 0;
}
abhiarora
  • 9,743
  • 5
  • 32
  • 57
0

First, as mentioned by @eerorika. Moving fundamental types is equivalent to copying. The reason of this behavior is pretty clear. Move semantic is developed for saving computational resource, and you clearly saved nothing (but wasted something) by clearing the value of an integer variable, which is not going to be further used. Leave it there is the best here.

Second, a "moved" variable is not necessarily "empty" or "cleared". It may be in any status formally, but for standard library objects there are some guarantees: (Quoted from here)

Unless otherwise specified, all standard library objects that have been moved from are placed in a valid but unspecified state. That is, only the functions without preconditions, such as the assignment operator, can be safely used on the object after it was moved from

As a result, you may see a "moved" std::vector contains random values and it is perfectly correct. Never assume such a std::vector is (or is not) empty because it might yields undefined behaviours. More generally, make no assumption (except the status is valid for standard library object) about the status of a object that was moved from.

LIU Qingyuan
  • 524
  • 5
  • 18