Упрощенная проблема:
#include <stdio.h>
class X
{
public:
int a = 0;
X() { printf("constr-def %d\n", a); }
X(int i):a(i+1) { printf("constr-i %d\n", a); }
int operator=(int i) { a = i; return i; }
operator bool() { return !!a; }
//~ X(const X& o) { a = o.a; printf("copy-constr %d\n", a); };
//~ const X& operator=(const X& o) { a = o.a; printf("copy-op %d\n", a); return *this; };
X(const X& o) = delete;
const X& operator=(const X& o) = delete;
};
int main() {
X x;
X y = 5; // should be equivalent to X y(5)
printf("results: %d %d\n", x.a, y.a);
}
Вывод при компиляции как есть с MSVC; или с активными вариантами конструктора gcc/MSVC и неудаленного копирования (прокомментировано выше):
>g++ -Os dede.cpp -o dede
>dede
constr-def 0
constr-i 6
results: 0 6
>Exit code: 0
--> Конструктор копирования никогда не используется; MSVC успешно компилируется как есть.
Скомпилируйте как есть с помощью gcc (текущая сборка v9.3.0 mingw/msys64):
>g++ -Os dede.cpp -o dede
dede.cpp: In function 'int main()':
dede.cpp:20:8: error: use of deleted function 'X::X(const X&)'
20 | X y = 5; // should be equivalent to X y(5)
| ^
dede.cpp:14:5: note: declared here
14 | X(const X& o) = delete;
| ^
dede.cpp:7:2: note: after user-defined conversion: 'X::X(int)'
7 | X(int i):a(i+1) { printf("constr-i %d\n", a); }
| ^
>Exit code: 1
Почему gcc выдает ошибку с конструктором копирования use of deleted
, когда он не использует его в неудаленном случае?
Попробуйте скомпилировать с помощью С++ 17, что гарантирует исключение копирования.
Кроме того, не оставляйте закомментированные строки в опубликованном коде. Оставление закомментированного кода приводит к путанице в отношении того, что вы действительно тестируете/наблюдаете.
Следуйте правилу «все или ничего», и ваш код станет лучше.
@PaulMcKenzie, «исходящий комментарий» здесь использовался, чтобы компактно показать оба варианта на месте (как уже упоминалось). Не случайный мусор.
До C++17 такая копировать элизию является оптимизацией, которая разрешена, но конструктор копирования (или перемещения) должен присутствовать и быть доступным.
Начиная с С++ 17 код работает нормально, потому что для принудительного исключения копирования конструкторы копирования/перемещения не должны снова присутствовать или быть доступными.
C++17 core language specification of prvalues and temporaries is fundamentally different from that of the earlier C++ revisions: there is no longer a temporary to copy/move from.
Что удивило, так это то, что правило, которое разрешало бы наблюдаемые побочные эффекты (printf copy-constr), здесь может быть только оптимизацией необязательный в рамках того же стандарта - таким образом, речь может идти о стандартной версии: > "Исключение копирования — единственная разрешенная форма оптимизации [...], которая может изменить наблюдаемые побочные эффекты."
Ваш код по-прежнему должен следовать правилам C++, независимо от того, какие оптимизации могут быть применены позднее компилятором.