c++ - 为什么在返回兼容类型时需要显式 std::move?

标签 c++ c++11 c++14 move-semantics

我正在看 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

See MCVE in action .

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/

相关文章:

c++ - 与 boost odeint 集成期间的析构函数调用

c++ - C++11 中的异步构造函数

c++ - std::map 与 std::unordered_map 迭代值类型的差异

c++ - 使用 utf8 格式的正则表达式过滤字符串

c++ - 如何为有符号整数类型创建用户定义的文字?

c++ - 找出1到10,000,000之间不是丑数的数

c++ - linux安装sdk后提示No such file or directory,如何解决?

c++ - Lambda 和线程

c++ - C++中复制构造函数的困惑

c++ - 如何检查类是否具有默认构造函数,公共(public)的、 protected 或私有(private)的