什么时候允许编译器优化复制构造函数
When is the compiler allowed to optimize out the copy-constructor
今天我遇到了一些关于拷贝构造函数我不太了解的事情。
考虑下一个代码:
#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;
}
然后在给copy赋值origin的时候调用了copy构造函数,这就说得通了。但是,如果我将副本声明更改为
static some_class copy = some_class();
它没有被调用。即使我使用 create()
函数,它也不会调用复制构造函数。
但是,将其更改为
时
static some_class copy = some_class().call();
它确实调用了复制构造函数。
一些研究解释说,允许编译器优化复制构造函数,这听起来是件好事。直到复制构造函数是非默认的,那么它可能会或可能不会做一些显而易见的事情,对吗?
那么什么时候允许编译器优化复制构造函数?
从C++17开始,这种copy elision是有保证的,编译器不仅允许,而且要求省略复制(或移动)构造,即使复制(或移动)构造有明显的副作用。
Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible, as the language rules ensure that no copy/move operation takes place, even conceptually:
In the initialization of a variable, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the
variable type:
T x = T(T(T())); // only one call to default constructor of T, to initialize x
In a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type:
T f() {
return T();
}
f(); // only one call to default constructor of T
您的代码片段与这两种情况完全匹配。
在 C++17 之前,不需要编译器,但允许省略复制(或移动)构造,即使 copy/move 构造函数具有可观察到的副作用。请注意,这是一种优化,即使发生复制省略,复制(或移动)构造函数仍然必须存在且可访问。
另一方面,call()
不符合复制省略的任何条件;返回的是*this
,既不是纯右值,也不是自动存期对象,需要复制构造,不能省略。
今天我遇到了一些关于拷贝构造函数我不太了解的事情。
考虑下一个代码:
#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;
}
然后在给copy赋值origin的时候调用了copy构造函数,这就说得通了。但是,如果我将副本声明更改为
static some_class copy = some_class();
它没有被调用。即使我使用 create()
函数,它也不会调用复制构造函数。
但是,将其更改为
static some_class copy = some_class().call();
它确实调用了复制构造函数。 一些研究解释说,允许编译器优化复制构造函数,这听起来是件好事。直到复制构造函数是非默认的,那么它可能会或可能不会做一些显而易见的事情,对吗? 那么什么时候允许编译器优化复制构造函数?
从C++17开始,这种copy elision是有保证的,编译器不仅允许,而且要求省略复制(或移动)构造,即使复制(或移动)构造有明显的副作用。
Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible, as the language rules ensure that no copy/move operation takes place, even conceptually:
In the initialization of a variable, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type:
T x = T(T(T())); // only one call to default constructor of T, to initialize x
In a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type:
T f() { return T(); } f(); // only one call to default constructor of T
您的代码片段与这两种情况完全匹配。
在 C++17 之前,不需要编译器,但允许省略复制(或移动)构造,即使 copy/move 构造函数具有可观察到的副作用。请注意,这是一种优化,即使发生复制省略,复制(或移动)构造函数仍然必须存在且可访问。
另一方面,call()
不符合复制省略的任何条件;返回的是*this
,既不是纯右值,也不是自动存期对象,需要复制构造,不能省略。