在 return 值上调用 std::move - 签名应该是什么
Calling std::move on a return value - what should the signature be
考虑
class X
{
public:
std::unique_ptr<int> m_sp;
A m_a;
A test1()
{
return std::move(m_a);
}
A&& test2()
{
return std::move(m_a);
}
std::unique_ptr<int> test3()
{
return std::move(m_sp);
}
std::unique_ptr<int>&& test4()
{
return std::move(m_sp);
}
std::unique_ptr<int> test5()
{
return std::make_unique<int>(50);
}
};
class A
{
public:
A()
{
m_i = 1;
}
A(A&& other)
{
this->m_i = other.m_i;
other.m_i = -1;
}
A& operator=(A&& other)
{
this->m_i = other.m_i;
other.m_i = -1;
return *this;
}
int m_i;
};
锻炼这些类
X x;
A y;
y.m_i = 10;
y = x.test1();
X x2;
A y2;
y2.m_i = 10;
y2 = x2.test2();
两者都调用 A 的移动赋值,但仅在 test1 情况下我们调用 A 的移动构造函数。这是为什么?是因为我们不能 return A&&(std::move 会将 A 转换为 A&&,但 test1 说它必须 return A)。
一般来说,当一个人想要move/transfer昂贵成员变量的所有权时,你想指定return是右值引用(A&&)还是左值(A)类型?
感觉有点不自然,就好像你没有使用成员变量,你让 RVO/NRVO 做这件事,只是 return 一个左值。以 unique_ptr 为例,当你有一个自动变量时,你有一个像 test5() 这样的签名,但是如果你有一个变量,不适合 RVO/NRVO,就像成员变量应该 test3或者test4的签名优先。
有兴趣知道。
谢谢
那里存在语义差异。当你 return 像
这样的对象时
A test1()
{
return std::move(m_a);
}
std::unique_ptr<int> test3()
{
return std::move(m_sp);
}
那你总是搬出你的会员。无论调用者是否使用 return 值做某事,您都会从 X
移出到一个临时值中。所有权不再属于你。调用者可以接管 return 值。如果调用者忽略 return 值,则临时值无论如何都会被销毁。另一方面,如果你 return 一个右值引用,比如
A&& test2()
{
return std::move(m_a);
}
std::unique_ptr<int>&& test4()
{
return std::move(m_sp);
}
您只是为调用者提供了移动 from/take 对象所有权的机会。如果调用者不执行移动,您的 X
将保留所有权,对象不会被移动。
要理解的关键是,与名称所暗示的相反,std::move()
实际上并不执行移动。它仅允许从中移动给定对象。实际移动由相应类型的移动构造函数或移动赋值运算符执行。
所以你的答案是:看你想表达什么。如果你 return 一个对象,你就是在说 "I'm throwing this away, if you want it: it's over there"。如果你 return 一个右值引用,你就是说 "that's the thing, now's your chance to take it, otherwise I keep it"…
考虑
class X
{
public:
std::unique_ptr<int> m_sp;
A m_a;
A test1()
{
return std::move(m_a);
}
A&& test2()
{
return std::move(m_a);
}
std::unique_ptr<int> test3()
{
return std::move(m_sp);
}
std::unique_ptr<int>&& test4()
{
return std::move(m_sp);
}
std::unique_ptr<int> test5()
{
return std::make_unique<int>(50);
}
};
class A
{
public:
A()
{
m_i = 1;
}
A(A&& other)
{
this->m_i = other.m_i;
other.m_i = -1;
}
A& operator=(A&& other)
{
this->m_i = other.m_i;
other.m_i = -1;
return *this;
}
int m_i;
};
锻炼这些类
X x;
A y;
y.m_i = 10;
y = x.test1();
X x2;
A y2;
y2.m_i = 10;
y2 = x2.test2();
两者都调用 A 的移动赋值,但仅在 test1 情况下我们调用 A 的移动构造函数。这是为什么?是因为我们不能 return A&&(std::move 会将 A 转换为 A&&,但 test1 说它必须 return A)。
一般来说,当一个人想要move/transfer昂贵成员变量的所有权时,你想指定return是右值引用(A&&)还是左值(A)类型?
感觉有点不自然,就好像你没有使用成员变量,你让 RVO/NRVO 做这件事,只是 return 一个左值。以 unique_ptr 为例,当你有一个自动变量时,你有一个像 test5() 这样的签名,但是如果你有一个变量,不适合 RVO/NRVO,就像成员变量应该 test3或者test4的签名优先。
有兴趣知道。
谢谢
那里存在语义差异。当你 return 像
这样的对象时A test1()
{
return std::move(m_a);
}
std::unique_ptr<int> test3()
{
return std::move(m_sp);
}
那你总是搬出你的会员。无论调用者是否使用 return 值做某事,您都会从 X
移出到一个临时值中。所有权不再属于你。调用者可以接管 return 值。如果调用者忽略 return 值,则临时值无论如何都会被销毁。另一方面,如果你 return 一个右值引用,比如
A&& test2()
{
return std::move(m_a);
}
std::unique_ptr<int>&& test4()
{
return std::move(m_sp);
}
您只是为调用者提供了移动 from/take 对象所有权的机会。如果调用者不执行移动,您的 X
将保留所有权,对象不会被移动。
要理解的关键是,与名称所暗示的相反,std::move()
实际上并不执行移动。它仅允许从中移动给定对象。实际移动由相应类型的移动构造函数或移动赋值运算符执行。
所以你的答案是:看你想表达什么。如果你 return 一个对象,你就是在说 "I'm throwing this away, if you want it: it's over there"。如果你 return 一个右值引用,你就是说 "that's the thing, now's your chance to take it, otherwise I keep it"…