为什么在返回兼容类型时需要显式std::move
Why explicit std::move is needed when returning compatible type?
我正在观看STL的"不要帮助编译器"演讲,他在幻灯片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
请参阅MCVE的实际操作。
get_pair
的结果是移动构造,这是预期的。NRVO可能也完全忽略了这一举措,但这与当前问题无关。
get_tuple_moved
的结果也是移动构造的,这是明确规定的。然而,get_tuple
的结果是复制构造的,对我来说这是完全不明显的
我认为传递给return
语句的任何表达式都可能被认为具有隐含的move
,因为编译器知道它无论如何都会超出范围。看来我错了。有人能澄清一下吗,这里发生了什么?
另请参阅相关但不同的问题:函数返回值何时应该使用std::move?
get_tuple()中的return语句应该使用move构造函数进行复制初始化,但由于返回表达式的类型和返回类型不匹配,因此选择了复制构造函数。C++14中有一个变化,现在有一个重载解析的初始阶段,当return语句只是在主体中声明的自动变量时,它将其视为右值。
相关措辞可在[class.copy]/p32:中找到
当满足省略复制/移动操作的标准[..]、时,或者当返回语句中的表达式是一个(可能带括号)id表达式,该表达式命名了在正文中声明了自动存储持续时间的对象[..]时,将首先执行为复制选择构造函数的重载解析,就好像该对象是由右值指定的一样。
所以在C++14中,所有输出都应该来自A.的move构造函数
clang和gcc的Trunk版本已经实现了这一更改。要在C++11模式中获得相同的行为,您需要在return语句中使用显式std::move()。
相关文章:
- Usages of std::move
- 在C++中对T*类型执行std::move的意外行为
- 关于std::move的使用,是否有编译警告
- 我应该实现右值推送功能吗?我应该使用std::move吗
- 通过实例理解std::move及其目的
- 返回一个带有 std::move 的对象并链接函数
- 当 std::move 与 C 样式数组或不移动对象时会发生什么
- std::move a const std::vector in a lambda capture
- "std::forward"和"std::move"真的不生成代码吗?
- 如何在没有 std::move 的情况下移动临时对象
- 关于在成员重载中使用 std::move() 的问题
- 显式清除 std::move 之后的源资源
- std::move 如何使原始变量的值无效?
- 复制elision、std::move和链式函数调用
- 如果真的需要std::move,我们应该什么时候声明右值refs
- 使用std::move将std::unique_ptr作为qt信号参数传递
- 无法使用带有 std::move 的自定义删除器插入 std::unique_ptr
- C++:我应该在 return 语句中显式使用 std::move() 来强制移动吗?
- 编译器是否足够聪明,以至于 std::move 变量超出范围?
- std::move() 或其在局部变量上的显式等价物可以允许 elision 吗?