38
  • What's difference between default copy and std::move in that example?
  • After move the object is there any dependence between new and old ones?
int main () {

    int a = 100;
    std::cout<<&a<<std::endl;

    auto a_copy = a;                 // deduced as int
    std::cout<<&a_copy<<std::endl;

    auto a_move = std::move(a);      // deduced as int
    std::cout<<&a_move<<std::endl;

};

output:

0x7fffffffe094
0x7fffffffe098
0x7fffffffe09c
Drunix
  • 3,313
  • 8
  • 28
  • 50
Person.Junkie
  • 1,816
  • 4
  • 21
  • 31
  • 14
    Be aware that `std::move` doesn't actually move anything. It just performs a cast to rvalue ref, and the result is helpfully an rvalue whereas simply naming `a` is not. These two facts are part of providing an object from which to move but are not actually the thing that moves from it. So, asking about "after `move`" is a red herring. – Lightness Races in Orbit Jan 11 '15 at 15:56

4 Answers4

40

In this example, there is no difference. We will end up with 3 ints with value 100. There could definitely be a difference with different types though. For instance, let's consider something like vector<int>:

std::vector<int> a = {1, 2, 3, 4, 5}; // a has size 5
auto a_copy = a;                      // copy a. now we have two vectors of size 5
auto a_move = std::move(a);           // *move* a into a_move

The last variable, a_move, takes ownership of a's internal pointers. So what we end up with is a_move is a vector of size 5, but a is now empty. The move is much more efficient than a copy (imagine if it was a vector of 1000 strings instead - a_copy would involve allocating a 1000-string buffer and copying 1000 strings, but a_move just assigns a couple pointers).

For some other types, one might be invalid:

std::unique_ptr<int> a{new int 42};
auto a_copy = a;            // error
auto a_move = std::move(a); // OK, now a_move owns 42, but a points to nothing

For many types, there's no difference though:

std::array<int, 100> a;
auto a_copy = a;            // copy 100 ints
auto a_move = std::move(a); // also copy 100 ints, no special move ctor

More generally:

T a;
auto a_copy = a;            // calls T(const T& ), the copy constructor
auto a_move = std::move(a); // calls T(T&& ), the move constructor
Barry
  • 286,269
  • 29
  • 621
  • 977
  • Thanks, your answer is really helpful. Is using `static_cast&&>` exactly same as std::move (In your first example)? – Person.Junkie Jan 11 '15 at 15:54
  • 2
    @Person.Junkie Yes, `std::move` just performs a static cast to rvalue ref, but definitely prefer `std::move()` over writing the cast - it's (a) less typing and (b) makes the intent of the line of code much clearer. – Barry Jan 11 '15 at 15:58
  • 2
    @Barry: I disagree - although it's much shorter it is a _lie_. – Lightness Races in Orbit Jan 11 '15 at 15:59
  • 4
    @LightnessRacesinOrbit would you prefer to see `std::move` renamed to something like `std::rref`? Because I know I would - `std::move` is extremely misleading. – mbgda Jan 11 '15 at 16:38
  • @mbgda: Yes. IIRC either Sutter or Meyers vaguely agrees with this as an observation in hindsight, though I can't find the relevant blog post at the moment. – Lightness Races in Orbit Jan 11 '15 at 16:41
  • @LightnessRacesinOrbit in his new book, Meyers takes the approach of just saying "there have been suggestions that a better name for it might have been something like `rvalue_cast`" – Barry Jan 11 '15 at 16:44
  • 3
    @Barry: Yeah, there we go; that's probably what I'm remembering. I believe it's a point that's been conceded a few times by various high-profile types (as opposed to being limited to forum posts on cprogramming.com). That is, "it signifies intent" is a cute principle that may work well in simple scripting languages, but it entirely defies logic (not to mention established paradigms) in something as flexible as C++. – Lightness Races in Orbit Jan 11 '15 at 16:45
  • 1
    Is move __guaranteed__ to work as copy for ints and other trivial types? – RiaD Jan 11 '15 at 19:31
  • 4
    The vector `a` is not guaranteed to be empty after move. – T.C. Jan 11 '15 at 20:38
  • @LightnessRacesinOrbit Well put. – Barry Jan 11 '15 at 22:40
  • @RiaD The term "trivial types" includes pointers, POD classes, arrays of such, etc. I believe the answer to your question ends up being somewhat circular - if move was not equivalent to copy for `T`, then `T` would not be a trivial type. – Barry Jan 11 '15 at 22:47
  • @T.C. [This answer](http://stackoverflow.com/a/17735913/2069064) from Howard Hinnant claims it is so guaranteed: "for the move constructor, yes, the moved-from vector will always be empty." – Barry Jan 11 '15 at 22:49
  • 1
    @Barry It's guaranteed to have constant complexity; but nothing in the standard prevents the implementation from adding a few (constant) number of elements into the moved-from vector just for fun :) – T.C. Jan 11 '15 at 23:37
  • @Barry: You're grossly misrepresenting that Hinnant answer. You didn't even quote the entirety of the sentence that you'd picked! Those words apply to one implementation in one scenario. There are another 20 or so paragraphs explaining how it is not this simple and how no such guarantee exists in general. Indeed, following your logic, I can now provide the following quote from that same answer: "the moved-from vector will in general not be empty". :) – Lightness Races in Orbit Jan 12 '15 at 19:45
  • @LightnessRacesinOrbit But I'm move-constructing it, aren't I? The 20 or so other paragraphs are about move-assignment - which does not appear in my example. Also, I only left out the word "So", that's pretty nitpicky to say I didn't quote the entire sentence... – Barry Jan 12 '15 at 19:51
  • @Barry: Oh. Well, granted. Then I shall simply fall back on the precise definition of "guaranteed": we usually mean "by the standard" when we say that, and this is not. – Lightness Races in Orbit Jan 12 '15 at 19:57
  • @LightnessRacesinOrbit Well, yes, per TC's comment it is technically not per-the-standard guaranteed although any implementation that leaves a non-empty moved-from vector during move-construction is doing something really bizarre. Seems like a safe assumption to make, as far as assumptions about what your compiler is doing are concerned. – Barry Jan 12 '15 at 20:00
  • Also `static_cast&&>` may perform implicit conversions if `a` is a different type (implicitly convertible to `vector`) so you would end up not moving from `a`. – Oktalist Jan 14 '15 at 15:49
16

Using std::move just changes an lvalue to an xvalue, so it is eligible for use with move constructors and move assignment operators. These do not exist for built-in types, so using move makes no difference in this example.

Drunix
  • 3,313
  • 8
  • 28
  • 50
8

What's difference between default copy and std::move in that example?

There is no difference. Copying something satisfies the requirements of a move, and in the case of built-in types, move is implemented as a copy.

After move the object is there any dependence between new and old

No, there are no dependencies. Both variables are independent.

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • ...and what is advantage of std::move and why? – Person.Junkie Jan 11 '15 at 15:45
  • 3
    @Person.Junkie Here? There is no advantage. There is the disadvantage that the code is confusing. – juanchopanza Jan 11 '15 at 15:46
  • In general? why exist std::move? I know that std::move convert lvalue/rvalue to rvalue using static_cast, can this 'cast' cause big improvement? – Person.Junkie Jan 11 '15 at 15:49
  • 5
    @Person.Junkie That is way too broad. There are whole papers about that. But basically, some types are cheap to move, and expensive to copy. In these cases there may be an advantage in moving. Also, some things are not copyable, but may be movable (e.g. a file stream or a unique pointer.) – juanchopanza Jan 11 '15 at 15:50
  • 7
    @Person.Junkie: The cast doesn't itself cause any "big improvement". The cast allows a programmer to conveniently indicate that they want a _different_ function called to implement the data transfer: conventionally, we write "move constructors" that move rather than copy, e.g. by swapping internal pointers. That can cause a _huge_ improvement, but `std::move` itself doesn't do that. You could do it in C++03 with tags and various other techniques, it just wasn't quite as convenient (and couldn't work generically e.g. elements in standard library containers). – Lightness Races in Orbit Jan 11 '15 at 15:58
5

To expand on the other poster's answer, the move-is-a-copy paradigm applies to all data structures composed of POD types (or composed of other types composed of POD types) as well, as in this example:

struct Foo
{
    int values[100];
    bool flagA;
    bool flagB;
};

struct Bar
{
    Foo foo1;
    Foo foo2;
};

int main()
{
    Foo f;
    Foo fCopy = std::move(f);
    Bar b;
    Bar bCopy = std::move(b);
    return 0;
}

In the case of both Foo and Bar there is no meaningful way to move the data from one to another because both are ultimately aggregates of POD types - none of their data is indirectly owned (points to or references other memory). So in these cases, the move is implemented as a copy and originals (f, b) remain unaltered after the assignments on the std::move() lines.

Move semantics can only be meaningfully implemented with dynamically allocated memory or unique resources.

mbgda
  • 787
  • 5
  • 8
  • 1
    *"there is no meaningful way to move the data from one to another because both are composed of value types"* -- I don't think value types is the correct term here. All of the standard library containers are value types, and there is certainly a meaningful way to move their data. – Benjamin Lindley Jan 11 '15 at 15:57
  • 1
    @BenjaminLindley They are not *composed* of value types though - they contain dynamically allocated memory (so, pointer types). – mbgda Jan 11 '15 at 15:57
  • Okay, but if I put a `std::vector` in a struct, like this: `struct Foo { std::vector v; };`, then that struct is composed of value types, and there is a meaningful way to move its data. – Benjamin Lindley Jan 11 '15 at 16:00
  • @mbgda: I don't think that's the correct definition of "value type", which conventionally refers to externally-visible semantics, not implementation details. Did you mean POD or Aggregate? – Lightness Races in Orbit Jan 11 '15 at 16:00
  • 1
    @BenjaminLindley I think you're being too pedantic about this - it's composed only of value types on the surface, but not through the entire composition chain. – mbgda Jan 11 '15 at 16:00
  • @LightnessRacesinOrbit perhaps it's not the strictly correct terminology - do you know what would apply in this case? – mbgda Jan 11 '15 at 16:01
  • @LightnessRacesinOrbit I meant POD types or aggregates of POD types (or aggregates of aggregates of POD types, etc). – mbgda Jan 11 '15 at 16:03
  • 1
    I like to call it "flat data" but I think I made that up. The point, of course, is that there's no indirection involved so if you can work that into the language somehow, you're sorted. – Lightness Races in Orbit Jan 11 '15 at 16:03
  • @LightnessRacesinOrbit The flat data thing is a decent colloquial term. I'm sure there's some terminology for it in the standard. I edited my answer to be a little more specific but it's definitely hard to put it in terms everyone would be happy with. – mbgda Jan 11 '15 at 16:13