带有右值引用的C++:std::move不是在移动内容

C++: std::move with rvalue reference is not moving contents

本文关键字:移动 move std 引用 C++      更新时间:2023-10-16

示例程序:

#include <iostream>
#include <string>
#include <vector>
template <typename T>
void print(const T& _vec)
    {        
        for( auto c: _vec )
            std::cout << c << ",";
    }
typedef std::vector<std::string> vecstr_t;
struct Trade
{
    explicit Trade(vecstr_t&& vec) : _vec(vec )
    {       
    }  
     vecstr_t _vec;
};

int main()
{   
    vecstr_t tmpV = {"ONE", "TWO", "THREE", "FOUR"};    
    std::cout << "size 1:" << tmpV.size() << "t"; print(tmpV); std::cout <<  "n" ;    
    Trade t(std::move(tmpV));    
    std::cout << "size 2:" << tmpV.size() << "t";  print(tmpV); std::cout <<  "n" ; // expted tmpV should be e,pty but it has original contents    
    print(t._vec);    
}

我预计尺寸2:应该为零,但输出为:

size 1:4    ONE,TWO,THREE,FOUR,
size 2:4    ONE,TWO,THREE,FOUR,
ONE,TWO,THREE,FOUR,
explicit Trade(vecstr_t&& vec) : _vec(vec)
{}

在上面的构造函数中,即使vec是对vecstr_t的类型右值引用,它本身也是一个左值。要记住的基本规则是——如果它有一个名称,它就是一个左值。

很少有上下文可以自动移动左值(例如按值返回对象的函数的return语句(,但构造函数的mem初始值设定项列表不是其中之一。

在您的示例中,_vec是从vec复制构建的。如果您希望它是移动构造的,请使用std::move

explicit Trade(vecstr_t&& vec) : _vec(std::move(vec))
{}

现在,对print的第二次调用将不会打印任何内容。请注意,从技术上讲,第二个调用可以打印非零大小,因为从vector移动的内容是未指定的。但在大多数(可能是所有(实现中,您将看到一个空的vector

现场演示


你在下面的评论说,你的意图是同时接受右值和左值,只在前者的情况下移动,否则复制论点。正如目前所写的,你的构造函数只接受右值,而不接受左值。有几种不同的选择可以实现你想要的。

最简单的方法可能是更改参数,使其按值接受参数,然后无条件地移动。

explicit Trade(vecstr_t vec) : _vec(std::move(vec))
{}

这种方法的缺点是,您可能需要额外的vector移动构造,但移动构造vector很便宜,在大多数情况下应该使用此选项。

第二个选项是创建构造函数的两个重载

explicit Trade(vecstr_t&&      vec) : _vec(std::move(vec)) {}
explicit Trade(vecstr_t const& vec) : _vec(vec)            {}

这种方法的缺点是,重载的数量将随着构造函数参数数量的增加而呈指数级增加。

第三种选择是使用完美的转发。

template<typename V>
explicit Trade(V&& vec) : _vec(std::forward<V>(vec)) {}

当构造函数将参数转发到构造_vec时,上面的代码将保留传递给构造函数的参数的值类别。这意味着,如果vec是右值,则将调用vecstr_t移动构造函数。如果它是一个左值,它将从中复制。

此解决方案的缺点是,构造函数将接受任何类型的参数,而不仅仅是vecstr_t,如果该参数不能转换为vecstr_t,则mem初始值设定项列表中的move/copy构造将失败。这可能会导致用户感到困惑的错误消息。