我有以下内容:
#include <iostream>
#include <utility>
class T {
public:
T() {std::cout << "Default constructor called" << std::endl;}
T(const T&) {std::cout << "Copy constructor called" << std::endl;}
};
static T s_t;
T foo() {
return s_t;
}
class A {
public:
A() = delete;
A(T t) : _t(t) {}
private:
T _t;
};
int main() {
std::cout << "Starting main" << std::endl;
A a(foo());
return 0;
}
当我用以下行编译它时:
g++ -std=c++17 -O3 test.cpp
并运行它,我得到以下输出
Default constructor called
Starting main
Copy constructor called
Copy constructor called
我的问题是:由于 A
的构造函数采用类型为 T
的右值对象,该对象仅用于初始化 _t
,编译器是否可以避免第二次复制操作,直接将s_t
复制到_t
中?
(我知道我可以通过为 T
实现 move 构造函数来用 move 替换第二个拷贝)
最佳答案
您的复制构造函数具有可观察到的副作用(输出)。 C++ 优化的(几乎)黄金法则是假设规则:不得更改可观察到的副作用。但是这个规则的两个异常(exception)之一是省略了一些复制操作,在此处指定:
http://eel.is/c++draft/class.copy.elision
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects.
但这只在非常特殊的情况下才允许:
This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
- in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object [...]
您的变量没有自动存储持续时间(即它不是局部变量)。
- in a throw-expression, [...]
我们不是抛出表达式。
- in a coroutine [...]
我们不在协程中。
- when the exception-declaration of an exception handler ([except]) declares an object of the same type (except for cv-qualification) as the exception object ([except.throw]) [...]
也不是这样的。
这里既没有涉及从函数返回全局变量,也没有涉及使用构造函数参数来初始化成员变量,因此任何编译器在您显示的代码中省略这些拷贝都是非法的。
就是说,如果编译器可以证明没有副作用(在大多数编译器的情况下,包括系统调用和内存分配),当然可以根据 as-if 规则随意删除拷贝,就像所有其他优化一样。
关于C++ 优化 : avoid a copy operation,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58119586/