今天,我遇到了一些我对复制构造函数不太了解的内容。
考虑下一个代码:
#include <iostream>
using namespace std;
class some_class {
public:
some_class() {
}
some_class(const some_class&) {
cout << "copy!" << endl;
}
some_class call() {
cout << "is called" << endl;
return *this; // <-- should call the copy constructor
}
};
some_class create() {
return some_class();
}
static some_class origin;
static some_class copy = origin; // <-- should call the copy constructor
int main(void)
{
return 0;
}
然后在为副本分配原点时调用副本构造函数,这很有意义。但是,如果我将复制声明更改为
static some_class copy = some_class();
它没有被调用。即使使用create()
函数,它也不会调用复制构造函数。
但是,将其更改为
static some_class copy = some_class().call();
它确实调用了复制构造函数。 一些研究解释说,允许编译器优化复制构造函数,这听起来不错。直到copy-constructor为非默认构造,否则它可能会或可能不会做明显的事情,对不对? 那么什么时候允许编译器优化复制构造函数?
答案 0 :(得分:4)
自C ++ 17起,保证了这种copy elision,不仅允许编译器使用,而且要求编译器省略复制(或移动)构造,即使复制(或移动)构造函数具有可观察到的副作用。
在以下情况下,即使复制/移动构造函数和析构函数具有明显的副作用,也要求编译器省略类对象的复制和移动构造。这些对象直接构造到存储中,否则会将它们复制/移动到其中。复制/移动构造函数不需要存在或不可访问,因为语言规则确保即使在概念上也不会发生复制/移动操作:
在变量的初始化中,当初始值设定项表达式是与()相同的类类型(忽略cv限定)的prvalue时。 变量类型:
T x = T(T(T())); // only one call to default constructor of T, to initialize x
在return语句中,当操作数是与函数返回类型相同的类类型(忽略cv限定)的prvalue时:
T f() { return T(); } f(); // only one call to default constructor of T
您的代码段完全符合这两种情况。
在C ++ 17之前,编译器不是必需的,但即使复制/移动构造函数具有明显的副作用,也可以省略复制(或移动)构造。请注意,这是一种优化,即使复制省略发生,复制(或移动)构造函数仍然必须存在并且可以访问。
另一方面,call()
与复制省略的任何条件都不匹配;返回*this
,它既不是prvalue也不是具有自动存储持续时间的对象,它是必需的复制结构,不能省略。