考虑以下代码:
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构造函数。正确吗?
答案 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
}
也
答案 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
。无论如何,这就是我过去所做的。