C ++ 11标准规定,如果满足复制省略的条件(§12.8/31
),则实现应将return
ed局部左值变量和函数参数视为rvalue (移动),如果重载决议没有详细说明,则应将其视为左值(副本)。
§12.8 [class.copy] p32
当满足或将满足复制操作的省略标准时,除了源对象是函数参数之外,并且要复制的对象由左值指定,重载决策要选择首先执行副本的构造函数,就像对象是由右值指定的一样。如果重载决策失败,或者所选构造函数的第一个参数的类型不是对象类型的rvalue引用(可能是cv-qualified),则再次执行重载决策,将对象视为左值。 [注意:无论是否发生复制省略,都必须执行此两阶段重载决策。如果未执行elision,它将确定要调用的构造函数,并且即使调用被省略,也必须可以访问所选的构造函数。 -end note ]
这是否也包含成员子对象?我测试了以下片段:
#include <iostream>
struct traced{
traced(){ std::cout << "default ctor\n"; }
traced(traced const&){ std::cout << "copy ctor\n"; }
traced(traced&&){ std::cout << "move ctor\n"; }
};
struct X{
traced t;
};
traced f(){
X x;
return x.t;
}
int main(){
traced t = f();
}
Live example on Ideone. GCC 4.7 ToT和Clang 3.1 ToT都不会显示“move ctor”,这使我相信该标准不包含成员子对象。
我忽视了什么吗?我的测试代码坏了吗?究竟是什么原因导致输出成为现实?
答案 0 :(得分:6)
返回子对象时,您无法忽略其构造。可以这样想:移动和复制elision本质上相当于在最终移动或复制到的位置构造对象。这适用于完整的对象,因为将留出适当的空间。它不适用于子对象,因为您将构造封闭对象。即使它与子对象具有相同的大小,即有足够的空间,封闭对象也会被破坏,并且可能对子对象做有趣的事情。
实际上,这意味着不能省略对象的构建。