返回本地对象是否需要移动语义?

时间:2015-03-20 10:19:51

标签: c++ c++11

当按值返回本地对象时,C ++编译器可以通过利用移动语义来优化不必要的副本(复制省略)。
可能优化”意味着如果不满足适当的条件,行为应该基于副本回退到默认的值语义返回。
因此,据我所知,按值返回可复制对象始终有效。

但编译器(clang和gcc)似乎不同意我的解释,如下面的MWE所示。

class Foo {
public:
    Foo();
    Foo(const Foo&);
    Foo(Foo&&) = delete;
}

Foo f() { return Foo(); }  // error: call to explicitly deleted constructor of 'Foo'
Foo g() { Foo a; return a; }  // gcc complains, clang is fine
Foo x = g();  // error: call to explicitly deleted constructor of 'A'

Q1:按值返回是否要求对象可移动?
Q2:如果没有,gcc和clang在我的MWE上行为不端或者我错过了其他的东西吗?

2 个答案:

答案 0 :(得分:32)

您只是满足重载决策的预期行为:Foo()是一个右值,因此重载决策会将构造函数Foo(Foo&&)视为最佳匹配。由于删除了该重载,因此您的程序格式错误。此外,还有一条特殊规则,即Foo a; return a;也会执行重载解析,就像a首先是右值一样。 (该规则基本适用于返回声明符合复制条件的情况。)

这一切都按预期工作。是你删除了重载,所以你明确要求禁止这样的结构。

请注意"真实"代码通常不会遇到这个障碍,因为只要你声明一个复制构造函数,你的类根本就不会有任何移动构造函数。但是你不好意思地说,"不,实际上我想要一个移动构造函数,如果有人试图使用它,我希望它是一个错误"。 / p>

答案 1 :(得分:8)

关于这个:

Foo g() { Foo a; return a; }  // gcc complains, clang is fine

海湾合作委员会是对的,由于[class.copy]/32(强调我的),这不应该编译:

  

当满足复制/移动操作的省略标准时,但不符合异常声明,以及   要复制的对象由左值指定,或者当return语句中的表达式为(可能是   parenthesized)id-expression命名一个具有在body中声明的自动存储持续时间的对象   最内层封闭函数或lambda表达式的参数声明子句,重载决策   首先执行选择复制的构造函数,就像对象是由右值指定的一样。如果   第一个重载决策失败或未执行,或者如果所选的第一个参数的类型   构造函数不是对象类型的rvalue引用(可能是cv-qualified),重载决策是   再次执行,将对象视为左值。 [注意: 这个两阶段重载决策必须是   无论是否会发生复制,都会执行。如果是elision,它确定要调用的构造函数   未执行,即使呼叫被取消,也必须可以访问所选的构造函数。 -end note ]

因此,实现应该为elision选择移动构造函数,并且在删除时,该程序是不正确的。