【问题标题】:Is it legal to elide a non-trivial copy/move constructor in initialization?在初始化中省略重要的复制/移动构造函数是否合法?
【发布时间】:2017-02-08 21:48:04
【问题描述】:

鉴于此应用程序:

#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

Live example

获得预期输出的唯一方法是使用 -fno-elide-constructors (example) 进行编译

我认为如果这样做会影响观察到的行为,编译器可能不会省略某些内容,但 GCC、clang 和 MSVC 似乎正在这样做。

我是否遗漏了一些一般规则,或者它是特定于使用临时对象初始化的?

【问题讨论】:

  • 欺骗/与this 和/或this 相关
  • 另外,X b = 2; 是初始化,无论如何都不会使用赋值运算符。

标签: c++ c++11 language-lawyer


【解决方案1】:

即使忽略副作用,也允许复制省略:

[class.copy]/31:当满足某些条件时,允许实现省略类的复制/移动构造 对象,即使为复制/移动操作选择了构造函数和/或对象的析构函数 有副作用。 [...]

一个好的一般规则是不要编写依赖于复制/移动构造函数副作用的代码,因为您很容易被省略。在 C++17 中尤其如此,其中某些复制省略是强制性的。

【讨论】:

  • 添加到这个,12.8.31.3:“当一个没有绑定到引用(12.2)的临时类对象将被复制/移动到具有相同简历的类对象时- 非限定类型,可以通过将临时对象直接构造到省略的复制/移动的目标中来省略复制/移动操作"
【解决方案2】:

引用the standard12.8.3:

当满足某些条件时,允许省略实现 类对象的复制/移动构造,即使构造函数 选择用于复制/移动操作和/或析构函数 对象有副作用。

(强调我的)

这意味着即使副本有副作用,编译器也可以删除副本。这正是您的情况。

【讨论】:

  • 在 C++17 草案中是 12.8.3。我问的是C+11。但我找到了,12.8.31。
  • @RustyX 是的,它(有点)在同一部分,都在 12.8.3X
猜你喜欢
  • 2023-03-03
  • 2018-09-17
  • 2021-03-24
  • 2020-04-26
  • 1970-01-01
  • 2014-01-02
  • 2014-09-07
  • 2011-09-10
相关资源
最近更新 更多