Является ли законным исключать нетривиальный конструктор copy/move при инициализации?

Учитывая это приложение:

#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 ()

Я думал, что компилятор может не удалиться, если это повлияет на наблюдаемое поведение, но GCC, clang и MSVC, похоже, делают именно это.

Мне не хватает какого-либо общего правила или оно специфично для инициализации объекта с помощью временного?

Ответ 1

Копирование elision разрешено, даже если оно игнорирует побочные эффекты:

[class.copy]/31: Когда выполняются определенные критерии, реализации разрешается опустить конструкцию копирования/перемещения класса object, , даже если конструктор, выбранный для операции копирования/перемещения и/или деструктор для объекта имеют побочные эффекты. [...]

Хорошим общим правилом является не писать код, который опирается на побочные эффекты конструктора copy/move, так как вы можете легко укусить его. Это особенно верно в С++ 17, где некоторые случаи копирования являются обязательными.

Ответ 2

Чтобы процитировать стандарт 12.8.3:

Когда выполняются определенные критерии, реализация может пропустить копирование/перемещение объекта класса, , даже если конструктор выбранных для операции копирования/перемещения и/или деструктора для объект имеет побочные эффекты.

(Акцент мой)

Это означает, что компилятору разрешено удалять копию, даже если копия имеет побочные эффекты. Это именно то, что происходит в вашем случае.