Приведенный ниже код компилируется нормально (см. ссылку на Голболта ниже):
#include <memory>
struct B;
struct A1 {
A1() = default;
~A1();
std::unique_ptr<B> ptr;
};
#if 0
struct A2 {
A2();
~A2();
std::unique_ptr<B> ptr;
};
A2::A2() = default;
#endif
int main()
{
}
Но если я заменю #if 0
на #if 1
для компиляции класса A2
, я получу следующую ошибку от gcc:
In file included from /opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/memory:76,
from <source>:1:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = B]':
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/unique_ptr.h:396:17: required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = B; _Dp = std::default_delete<B>]'
<source>:17:1: required from here
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/unique_ptr.h:93:23: error: invalid application of 'sizeof' to incomplete type 'B'
93 | static_assert(sizeof(_Tp)>0,
| ^~~~~~~~~~~
ASM generation compiler returned: 1
In file included from /opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/memory:76,
from <source>:1:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = B]':
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/unique_ptr.h:396:17: required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = B; _Dp = std::default_delete<B>]'
<source>:17:1: required from here
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/unique_ptr.h:93:23: error: invalid application of 'sizeof' to incomplete type 'B'
93 | static_assert(sizeof(_Tp)>0,
| ^~~~~~~~~~~
Execution build compiler returned: 1
Я получаю аналогичный результат на MSVC.
Я также получаю этот результат независимо от того, компилирую ли я как C++17 или C++20.
Мой вопрос:
Единственная разница между A1
и A2
заключается в определении конструктора внутри определения класса или вне его (в обоих случаях он определяется как default
).
Почему в этом случае есть разница?
Этот вопрос является продолжением этого поста: Почему unique_ptr требует полного типа в конструкторе?
Когда A2 компилируется, компилятор не знает, что вы собираетесь использовать конструктор по умолчанию позже. Это имеет значение, но я не могу объяснить, что именно.
= default
отвлекающий маневр. Есть некоторые причуды вокруг этого, но они не имеют ничего общего с этой ситуацией. По умолчанию в заголовке это так же хорошо, как и встроенное определение пользователем в большинстве случаев. Установка по умолчанию в другом TU так же хороша, как и пользовательское определение в другом TU.
Этот термин предоставляется пользователем, что находится где-то между неявно определенным и определяемым пользователем. И поставляется с некоторыми дополнительными правилами...
Вот разница: в A1
, где конструктор по умолчанию используется по умолчанию при его первом объявлении, компилятор фактически не определяет его до тех пор, пока его определение не потребуется, и, поскольку вы ни в коем случае не пытаетесь создать объект A1
, определение конструктора по умолчанию никогда не требуется в этой единице перевода, поэтому компилятор никогда не генерирует определение. Что касается A2
, нестандартное определение
A2::A2() = default;
на самом деле генерирует определение для функции, где это происходит. В этот момент B
должно быть завершено, но это не так. Это объясняется в связанном вопросе.
[...] Функция предоставляется пользователем, если она объявлена пользователем, а не явно задана по умолчанию или удалена при ее первом объявлении. Предоставленная пользователем функция с явным значением по умолчанию (т. е. явно установленная по умолчанию после ее первого объявления) неявно определяется в точке, где она явно установлена по умолчанию; если такая функция неявно определена как удаленная, программа некорректна. Непредоставленная пользователем функция по умолчанию (т. е. неявно объявленная или явно заданная по умолчанию в классе), которая не определена как удаленная, неявно определена, когда она используется odr ([basic.def.odr]) или требуется для постоянной оценки ([ выр.конст]).
Эх, кажется, я только что ответил на ваш вопрос в предыдущем.