Pytanie Czy legalne jest, aby podczas inicjalizacji usunąć nietrywialny konstruktor copy / move?


Biorąc pod uwagę tę aplikację:

#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;
}

Spodziewałem się, że wyjście będzie:

a.x=2
b.x=1003
c.x=1004
d.x=12

Jednak otrzymuję:

a.x=2
b.x=3
c.x=4
d.x=12

Przykład na żywo

Jedynym sposobem uzyskania oczekiwanego wyniku jest kompilacja -fno-elide-constructors (przykład)

Sądziłem, że kompilator nie może wymazać rzeczy, jeśli to zrobi, wpłynie na obserwowane zachowanie, ale GCC, clang i MSVC wydają się właśnie to robić.

Czy brakuje mi jakiejś ogólnej zasady, czy jest to specyficzne dla inicjowania obiektu za pomocą tymczasowego?


11
2018-02-08 13:18


pochodzenie


dupe / powiązane z to i / lub to - NathanOliver
Również, X b = 2; jest inicjalizacją i nigdy nie używa operatora przypisania. - Bo Persson


Odpowiedzi:


Kopiowanie elizacji może nastąpić, nawet jeśli ignoruje efekty uboczne:

[class.copy]/31: Gdy spełnione są określone kryteria, implementacja może pomijać konstrukcję klasy copy / move   obiekt, nawet jeśli konstruktor wybrany dla operacji kopiowania / przenoszenia i / lub destruktora dla obiektu   mieć efekty uboczne. [...]

Dobrą ogólną zasadą jest nie pisanie kodu, który opiera się na efektach ubocznych konstruktora kopiowania / przenoszenia, ponieważ łatwo można go ukąsić przez elizację. Jest to szczególnie prawdziwe w C ++ 17, gdzie pewne przypadki kopiowania są obowiązkowe.


9
2018-02-08 13:21



Aby dodać do tego, 12.8.31.3: "gdy obiekt klasy tymczasowej, który nie został powiązany z odwołaniem (12.2), zostanie skopiowany / przeniesiony do obiektu klasy z tym samym typem niekrewifikowanym cv, operacja kopiowania / przenoszenia może zostać pominięta przez skonstruowanie obiektu tymczasowego bezpośrednio do celu z pominiętej kopii / przeniesienia" - rustyx


Cytować standard 12.8.3:

W przypadku spełnienia określonych kryteriów implementacja może zostać pominięta   konstrukcja kopiuj / przenieś obiektu klasy, nawet jeśli konstruktor   wybrane dla operacji kopiowania / przenoszenia i / lub destruktora dla   obiekt ma efekty uboczne.

(Podkreśl moje)

Oznacza to, że kompilator może usunąć kopię, nawet jeśli kopia ma efekty uboczne. Co dokładnie dzieje się w twoim przypadku.


4
2018-02-08 13:21



To jest 12.8.3 w szkicu C ++ 17. Pytałem o C + 11. Ale znalazłem to, 12.8.31. - rustyx
@RustyX Tak, jest (rodzaj) w tej samej sekcji, zarówno w 12.8.3X - Sombrero Chicken