1

I understand that using v.push_back(std::move(foo)) will move foo contents to a vector.
However I do not understand why the below code :

#include <iostream>
#include <vector>

struct Foo {
    std::string s;
    float       f;
    friend std::ostream& operator<<(std::ostream& stream,const Foo& foo) {
        stream << "{" << foo.s << ":" << foo.f << "}";
        return stream;
    }
};


int main() {
    Foo foo{"Hello",2.0f};
    std::vector<Foo> v;
    v.push_back(foo);
    std::cout << foo << std::endl;
    v.push_back(std::move(foo));
    std::cout << foo << std::endl;
    std::cout << v[0] << v[1] << std::endl;
}

produces this output :

{Hello:2}
{:2}
{Hello:2}{Hello:2}

Why 2 is not "moved" ?

coincoin
  • 4,595
  • 3
  • 23
  • 47
  • Under what circumstances do you think printing a float variable would yield an empty string? – Rob Kennedy Jun 15 '15 at 23:04
  • You can't really "move" a basic type. What would be left? It's more about moving the data pointed to by resource handles from one owner to another. – ooga Jun 15 '15 at 23:06
  • You can't move basic types like `float`. If you can't move something it does a copy instead. I'd recommend having a look at [What are move semantics?](http://stackoverflow.com/q/3106110) since it seems like you're misunderstanding a basic concept of `move`. – AliciaBytes Jun 15 '15 at 23:08
  • What would it mean to "move" a 2 exactly? – David Schwartz Jun 15 '15 at 23:08

1 Answers1

4

You create a variable foo

Foo foo{"Hello",2.0f};

Then declare a vector

std::vector<Foo> v;

Then call push_back which invokes a copy of your Foo

v.push_back(foo);
std::cout << foo << std::endl;

Then you std::move(foo), which invalidates your foo instance

v.push_back(std::move(foo));

Trying to cout foo is now undefined behavior, as the internals are may be garbage.

std::cout << foo << std::endl;

While you technically could std::move a primitive, most implementations will simply invoke a copy because it is cheaper and faster.

Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
  • Your explanation is correct, but the author asks why integer "2" cannot be moved. – Oleg Andriyanov Jun 15 '15 at 23:07
  • The internals don't necessarily need to be garbage. Can you give a reference to the standard? – ooga Jun 15 '15 at 23:09
  • As an edit, I added a link that talks about what happens when you try to `move` a primitive. Hopefully that helps clear up what is happening in this specific case. – Cory Kramer Jun 15 '15 at 23:10
  • Thank you that clear up especially with the extra link mentionning ` copying is the fastest way to implement moving` – coincoin Jun 15 '15 at 23:11
  • 1
    How is that UB? A moved-from `std::string` is in a valid but unspecified state, but printing a `std::string` has no preconditions. As to `float`, the standard requires it to essentially be a copy. Initializing a new `float` causes a lvalue-to-rvalue conversion (which is really a glvalue-to-prvalue conversion) on the source `float` glvalue; that conversion is not going to change the source. – T.C. Jun 15 '15 at 23:38
  • @T.C., I was wondering about that. I went through the text and didn't see anything saying the source is modified for `float`. An answer backing that up would be welcome. – chris Jun 15 '15 at 23:46
  • @chris: remember: `std::move` doesn't actually move anything. It just casts the operand to an rvalue reference. For something like a constructor, that'll select the overload that takes an rvalue reference, and for some type that contains a pointer, that'll do move construction. In the case of a `float`, there's no `float::float(float &&)` to be selected, so nothing involved that can modify the source. – Jerry Coffin Jun 16 '15 at 00:37
  • @JerryCoffin, Yeah, that makes sense. I mean the standard could have specified that direct-initializing a `float` from an xvalue `x` sets `x` to 0 regardless, but a lot of people would then be paying for what they don't use and I'm sure some would argue that copying `x` is more useful. – chris Jun 16 '15 at 00:41
  • @chris: Yeah, I suppose it could, but I don't see any reason to do so. Along with probably reducing efficiency, it immediately seems like there could be some corner cases where it could break things. I haven't thought through the latter to be sure they exist, but I'm not at all sure they don't either. – Jerry Coffin Jun 16 '15 at 00:47