在C ++中使用移动语义的正确方法是什么?

时间:2018-09-16 11:30:47

标签: c++ stl move move-semantics

考虑以下代码:

class A {
private:
    std::string data;
public:
    void set_data(std::string&& data) {
        this->data = std::move(data); // line 6
    }
};

int main() {
    std::string move_me = "Some string";
    A a;
    a.set_data(std::move(move_me)); // line 13
}

我了解我们需要在第13行调用std::move(),以便将左值转换为右值引用(听起来正确吗?我是新手)。

但是,在第6行,我们是否需要再次使用std::move()?我假设不是,因为我们已经传递了右值引用,并且将调用std::string的move构造函数。正确吗?

3 个答案:

答案 0 :(得分:5)

  

但是,在第6行,我们是否需要再次使用std::move()

。为什么?因为在set_data中,data(自变量)是一个左值,因为它有一个名称。这两个std::move都是将move_me实际移到data中的a所必需的。

std::move行中没有6的情况下,move_me将不会移动,因为它将调用std::string(const std::string&),而不是std::string(std::string&&)

请记住-如果某物具有名称,则它是左值。

答案 1 :(得分:1)

似乎两个答案都是正确的,我只是在标准中添加了一段,解释了为什么在行std::move()和行#6中使用#13是正确的,以及为什么它是左值即使类型是#6行中的右值。

  

表达式的类型是标识符的类型。结果是由标识符表示的实体。如果实体是函数,变量或数据成员,则结果为左值,否则为prvalue。   5.1.1[expr.prim.general]/8

因此,从标准中应用此规则,我们有望得到正确的答案。

左值

    // move_me is identifier of a variable denotes to itself the result is lvalue
    std::string move_me = "Some string";

右值

   // constructing temporary e.g no  identifier is an rvalue
   std::string("Some string") ; 

左值

  // the variable data has type rvalue reference to move_ms, it denotes entity move_ms
  // the result is lvalue
  void set_data(std::string&& data);

左值

// the variable data has type  lvalue reference to move_ms, 
//it denotes entity move_ms the result is lvalue
void set_data(std::string& data);

左值或右值-通用引用

//the variable data has type universal reference it either holds lvalue or rvalue
template<typename T> void setdata(T && data) ;

因此,右值引用不是右值,可能会出错

Base(Base const & rhs); // non-move semantics
Base(Base&& rhs); // move semantics 

如果您错过使用std :: move()

 Derived(Derived&& rhs) : Base(rhs) // wrong: rhs is an lvalue
 {
  // Derived-specific stuff
 }

正确的版本是:

  Derived(Derived&& rhs) : Base(std::move(rhs)) // good, calls Base(Base&& rhs)
  {
  // Derived-specific stuff
  }

  • 创建对左值的左值引用-确定
  • 创建对右值的右值引用-确定
  • 创建对rvalue的左值const引用-确定
  • 创建对rvalue的左值引用-编译错误

答案 2 :(得分:-1)

#6行和#13行中都需要它。

斯科特·梅耶斯(Scott Mayers)对此事发了nice post

最可接受的方法是

// 1: full flexibility for the caller to decide where the data state comes from
struct X
{
    Y data_;
    explicit X(const Y& data) : data_(data) { }
    explicit X(Y&& data) : data_(std::move(data)) { }
};

// 2: forced copy or move at the call site and zero-copy move into the internal state of the X
struct X
{
    Y data_;
    explicit X(Y data) : data_(std::move(data)) { }
};

// 3: same as the setter below, but can have quite different forms based on what exactly is required
struct X
{
    Y data_;
    template <class... Z>
    explicit X(Z&&... arg) : data_(std::forward<Z>(args)...) { }
}

设置器最好以“透明”样式完成,有效地委派给该字段的赋值运算符。

template <typename Arg> void setData(Arg&& arg) {
    data_ = std::forward<Arg>(arg);
}

我建议编写一个简单的类,编写带有调试打印的各种复制/移动构造函数/运算符的代码,并稍加使用此类,以开发出如何使用&&,{{1 }}和std::forward。无论如何,这就是我过去所做的。