我有一个简单的片段:
class Object
{
private:
int value;
public:
Object(int value) : value(value) { cout << "Object::ctorn"; }
Object(const Object& obj) { cout << "Object::copy-ctorn"; }
Object(Object&& obj) { cout << "Object::move-ctorn"; }
};
Object take_and_return_obj(Object o) { return o; }
int main()
{
Object o(5);
take_and_return_obj(o);
}
现在,正如预期的那样,打印一个copy
和move
构造函数。
Object::copy-ctor
Object::move-ctor
这是因为使用copy-ctoro
被复制到函数,然后被回收使用以来move-ctor函数,返回值是一个xvalue。
但是,当函数的初始参数也是一个xvalue时,会发生一些事情:
int main()
{
Object o = take_and_return_obj(Object(5));
}
结果是,当值被发送给函数时,什么也没发生:
Object::ctor
Object::move-ctor
我假设move
用于返回操作,因此不受此更改的影响。但是,没有调用复制函数来在函数范围内创建0。我知道它不是任何类型的指针或引用,因为我让函数按值接受参数。
所以我的问题是:我在main
中创建的xvalue究竟发生了什么,以便函数内的参数得到它的值?
这是一个更有教育意义的问题,所以不要害怕进行更深入的回答。
是,在take_and_return_obj(Object(5));
中,省略了构造参数o
的复制/移动操作;
在下列情况下,编译器被要求省略类对象的复制和移动构造,即使复制/移动构造函数和析构函数具有可观察到的副作用。对象直接构造到存储中,否则它们将被复制/移动到存储中。复制/移动构造函数不需要存在或可访问:
……
…在对象的初始化中,当初始化表达式是与变量类型相同的类类型(忽略cv-限定)的右值时:
T x = T(T(f())); // only one call to default constructor of T, to initialize x
这个答案是为那些不知道术语"复制-省略"的人准备的。
这个问题由这个答案(包括两个相连的答案)回答。
在某些情况下,编译器可能会忽略复制内容。这种情况包括从函数获取或返回x值等场景。在这些情况下,不会调用copy/move函数,这样就不会创建对象的额外副本。