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

问题描述 投票:1回答:3

考虑以下代码:

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的移动构造函数。那是对的吗?

c++ stl move move-semantics
3个回答
5
投票

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

是。为什么?因为在set_data里面,data(参数)是一个左值,因为它有一个名字。 std::moves都需要在move_me中将data移动到a

没有std::move在线6move_me将不会被移动,因为那会叫std::string(const std::string&),而不是std::string(std::string&&)

记住 - 如果有东西有名字,那就是左值。


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) ;

所以,rvalue引用不是rvalue,事情可能会出错

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
  }

  • 创建左值的左值引用 - 好的
  • 创建对右值的右值引用 - 好的
  • 创建左值const对rvalue的引用 - 好的
  • 创建对rvalue的左值引用 - 编译ERROR

-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);
}

我建议使用各种复制/移动构造函数/运算符来编写一个简单的类,其中包含调试版本并稍微使用这个类来开发如何使用&&std::forwardstd::move的直觉。无论如何,这就是我在过去所做的事情。

© www.soinside.com 2019 - 2024. All rights reserved.