我正在看 STL 的“Don’t Help the Compiler”演讲,他在幻灯片 26 上有一个类似的例子:
struct A
{
A() = default;
A(const A&) { std::cout << "copied" << std::endl; }
A(A&&) { std::cout << "moved" << std::endl; }
};
std::pair<A, A> get_pair()
{
std::pair<A, A> p;
return p;
}
std::tuple<A, A> get_tuple()
{
std::pair<A, A> p;
return p;
}
std::tuple<A, A> get_tuple_moved()
{
std::pair<A, A> p;
return std::move(p);
}
有了这个,下面的调用:
get_pair();
get_tuple();
get_tuple_moved();
产生这个输出:
moved
moved
copied
copied
moved
moved
get_pair
的结果是 move 构造的,这是预期的。 NRVO 也可能完全省略了 move ,但这不在当前问题的主题范围内。
get_tuple_moved
的结果也是 move 构造的,这是明确指定的。但是,get_tuple
的结果是复制构造的,这对我来说是完全不明显的。
我认为传递给 return
语句的任何表达式都可以被认为具有隐式 move
,因为编译器知道它即将超出范围.好像我错了。有人可以澄清一下,这是怎么回事吗?
另见相关但不同的问题:When should std::move be used on a function return value?
最佳答案
get_tuple() 中的 return 语句应该使用 move 构造函数进行复制初始化,但由于返回表达式的类型和返回类型不匹配,因此选择了复制构造函数。 C++14 中进行了更改,现在有一个重载决议的初始阶段,当 return 语句只是在主体中声明的一个自动变量时,它将 return 语句视为右值。
相关写法可以在[class.copy]/p32中找到:
When the criteria for elision of a copy/move operation are met, [..], or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body [..], overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.
所以在 C++14 中,所有输出都应该来自 A 的 move 构造函数。
clang 和 gcc 的主干版本已经实现了这个改变。要在 C++11 模式下获得相同的行为,您需要在 return 语句中使用显式 std::move()。
关于c++ - 为什么在返回兼容类型时需要显式 std::move?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29717144/