鉴于此申请:
#include <iostream>
struct X {
X(int _x) { x = _x + 1; }
X(const X& that) { x = that.x + 10; }
X& operator=(const X& that) { x = that.x + 100; return *this; }
X(X&& that) { x = that.x + 1000; }
X& operator=(X&& that) { x = that.x + 10000; return *this; }
int x;
};
int main() {
X a(1);
std::cout << "a.x=" << a.x << std::endl;
X b = 2;
std::cout << "b.x=" << b.x << std::endl;
X c = X(3);
std::cout << "c.x=" << c.x << std::endl;
X d = a;
std::cout << "d.x=" << d.x << std::endl;
}
我希望输出为:
a.x=2
b.x=1003
c.x=1004
d.x=12
但我得到的是:
a.x=2
b.x=3
c.x=4
d.x=12
获得预期输出的唯一方法是使用-fno-elide-constructors
(example)进行编译
我认为如果这样做会影响观察到的行为,编译器可能不会忽略,但是GCC,clang和MSVC似乎只是这样做。
我是否缺少一些通用规则,还是特定于临时对象初始化?
答案 0 :(得分:9)
即使忽略副作用,也允许复制省略:
[class.copy]/31:
当满足某些条件时,允许实现省略类的复制/移动构造 object,即使是为复制/移动操作选择的构造函数和/或对象的析构函数 有副作用。 [...]
一个好的一般规则是不要编写依赖于复制/移动构造函数副作用的代码,因为你很容易被elision咬掉。在C ++ 17中尤其如此,其中复制省略的某些情况是强制性的。
答案 1 :(得分:4)
引用the standard 12.8.3:
当满足某些条件时,允许省略实现 复制/移动类对象的构造,即使构造函数也是 选择用于复制/移动操作和/或析构函数 对象有副作用。
(强调我的)
这意味着即使副本有副作用,也允许编译器删除副本。这正是你案件中发生的事情。