Разница между определениями конструктора класса, содержащего std::unique_ptr, и неполным типом

Приведенный ниже код компилируется нормально (см. ссылку на Голболта ниже):

#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 требует полного типа в конструкторе?

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

Passer By 10.02.2023 10:40

Когда A2 компилируется, компилятор не знает, что вы собираетесь использовать конструктор по умолчанию позже. Это имеет значение, но я не могу объяснить, что именно.

BoP 10.02.2023 10:42
= default отвлекающий маневр. Есть некоторые причуды вокруг этого, но они не имеют ничего общего с этой ситуацией. По умолчанию в заголовке это так же хорошо, как и встроенное определение пользователем в большинстве случаев. Установка по умолчанию в другом TU так же хороша, как и пользовательское определение в другом TU.
Passer By 10.02.2023 10:44

Этот термин предоставляется пользователем, что находится где-то между неявно определенным и определяемым пользователем. И поставляется с некоторыми дополнительными правилами...

BoP 10.02.2023 10:52
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
4
92
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот разница: в A1, где конструктор по умолчанию используется по умолчанию при его первом объявлении, компилятор фактически не определяет его до тех пор, пока его определение не потребуется, и, поскольку вы ни в коем случае не пытаетесь создать объект A1, определение конструктора по умолчанию никогда не требуется в этой единице перевода, поэтому компилятор никогда не генерирует определение. Что касается A2, нестандартное определение

A2::A2() = default;

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

См. [dcl.fct.def.default]/5:

[...] Функция предоставляется пользователем, если она объявлена ​​пользователем, а не явно задана по умолчанию или удалена при ее первом объявлении. Предоставленная пользователем функция с явным значением по умолчанию (т. е. явно установленная по умолчанию после ее первого объявления) неявно определяется в точке, где она явно установлена ​​по умолчанию; если такая функция неявно определена как удаленная, программа некорректна. Непредоставленная пользователем функция по умолчанию (т. е. неявно объявленная или явно заданная по умолчанию в классе), которая не определена как удаленная, неявно определена, когда она используется odr ([basic.def.odr]) или требуется для постоянной оценки ([ выр.конст]).

Другие вопросы по теме