0

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?

r18ul
  • 1,082
  • 2
  • 12
  • 30
  • 1
    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 Feb 10 '20 at 09:19
  • Related: https://stackoverflow.com/questions/7510182/how-does-stdmove-transfer-values-into-rvalues – Timo Feb 10 '20 at 09:19
  • 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 Feb 10 '20 at 09:25

2 Answers2

3

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_));
    ...
}
dewaffled
  • 2,850
  • 2
  • 17
  • 30
  • To add to this, one should note that the one actually transferring the ownership of the data is the move constructor. – theWiseBro Feb 10 '20 at 09:28
  • Is it not std::forward which should be used to give a better readability in that case? – Klaus Feb 10 '20 at 09:28
  • @Klaus `vec_` is not a forwarding reference so I'd say `move` is better suited. – Timo Feb 10 '20 at 09:47
  • @Klaus It is true that std::forward will work as well but we should understand the purpose of std::forward i.e. "perfect-forwarding". Here vec_ is an rvalue ref, so it's better to just straightaway use std::move, which is meant for this. – theWiseBro Feb 10 '20 at 10:27
  • @theWiseBro: OK, got it! Thanks! – Klaus Feb 10 '20 at 11:42
1

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_).

Timo
  • 9,269
  • 2
  • 28
  • 58