1
votes

If I use std::move on a stack object in the current scope, the contents are moved to destination leaving the source empty.

#include <iostream>
#include <string>
#include <utility>
#include <vector>

int main()
{
    std::string str("stackoverflow");

    std::vector<std::string> vec;
    vec.emplace_back(std::move(str));
    std::cout << "vec[0]: " << vec[0] << std::endl;

    std::cout << "str: " << str << std::endl;
}

Result:

vec[0]: stackoverflow
str: 

If I use the std::move for a rvalue or const lvalue function arguments, the contents are copied.

#include <iostream>
#include <memory>
#include <vector>
#include <utility>

void process_copy(std::vector<int> const & vec_)
{
    std::vector<int> vec(vec_);
    vec.push_back(22);
    std::cout << "In process_copy (const &): " << std::endl;
    for(int & i : vec)
        std::cout << i << ' ';
    std::cout << std::endl;
}

void process_copy(std::vector<int> && vec_)
{
    std::vector<int> vec(vec_);
    vec.push_back(99);
    std::cout << "In process_copy (&&): " << std::endl;
    for(int & i : vec)
        std::cout << i << ' ';
    std::cout << std::endl;
}

int main()
{
    std::vector<int> v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    process_copy(std::move(v));

    std::cout << "In main: " << std::endl;
    for(int & i : v)
        std::cout << i << ' ';
    std::cout << std::endl;
    std::cout << "size: " << v.size() << std::endl;
}

Result:

In process_copy (&&): 
0 1 2 3 4 5 6 7 8 9 99 
In main: 
0 1 2 3 4 5 6 7 8 9 
size: 10

Why is the behavior of std::move different?

2
Remember that std::move doesn't actually move anything. It is just a cast to rvalue reference. Whether that rvalue reference is subsequently moved or copied depends on context and whether or not the object implements move operations and whether or not the source object is const. - Jesper Juhl
In the first example, the one actually moving the ownership of the data is the move constructor of the std::string. In the second example, you're just sending an rvalue ref. You can manually take the ownership of the passed std::vector here. - theWiseBro

2 Answers

1
votes

Your vector is actually copied, not moved. The reason for this is, although declared as an rvalue reference, vec_ denotes an lvalue expression inside the function body. Thus the copy constructor of std::vector is invoked, and not the move constructor. The reason for this is, that vec_ is now a named value, and rvalues cannot have names, so it collapses to an lvalue. The following code will fail to compile because of this reason:

void foo(int&& i)
{
    int&& x = i;
}

In order to fix this issue, you have to make vec_ nameless again, by calling std::move(vec_).

3
votes

You need to use std::move if the value is bound to a variable even it is declared as rvalue revefernce (&&). i.e. it should be:

void process_copy(std::vector<int> && vec_)
{
    std::vector<int> vec(std::move(vec_));
    ...
}