Чому мій клас не може бути сконструйований за замовчуванням?


28

У мене є такі заняття:

#include <type_traits>

template <typename T>
class A {
public:
    static_assert(std::is_default_constructible_v<T>);

};

struct B {
   struct C {
      int i = 0;
   };

    A<C> a_m;
};

int main() {
    A<B::C> a;
}

Під час компіляції a_mне може бути сконструйовано за замовчуванням, але aє.

При зміні Cна:

struct C {
      int i;
   };

все добре.

Тестовано з Clang 9.0.0.


3
GCC 8.3 - Гаразд, GCC 9.1 / 9.2 - Невдача.
Evg

2
З C() {}ним теж працює.
Evg

3
Це мені пахне баггі. Немає очевидних матчів на Bugzilla.
Гонки легкості на орбіті

2
Цікаво: static_assertу Aпрограванні не вдається, але якщо ви замість цього замовчуєте конструювати Tвнутрішню частину A(наприклад, помістіть T t;туди члена ), це працює добре. Невідповідність між тим, що вам говорить ознака типу, і тим, що насправді можливо ...
sebrockm

2
@Nicolas Правда, але це через деякі крайні випадки, жоден з яких не застосовується тут (зокрема, як говорить те саме речення щодо cppreference, const int x;недійсний без ініціалізатора, суто через constповедінку ініціалізації вбудованих типів та деяких історія)
Гонки легкості на орбіті

Відповіді:


9

Це заборонено як текстом стандарту, так і кількома основними реалізаціями, як зазначено в коментарях, але з абсолютно незв'язаних причин.

По-перше, причина "за книгою": точка моменту описується A<C>, згідно стандарту, безпосередньо перед визначеннямB , а точка інстанції std::is_default_constructible<C>безпосередньо перед цим:

Для спеціалізації шаблону класу, [...] якщо спеціалізація неявно створена, оскільки на неї посилається всередині іншої спеціалізації шаблону, якщо контекст, на який посилається спеціалізація, залежить від параметра шаблону, і якщо спеціалізація не є передбачуваною попередньою до моменту шаблону, що додається, точка інстанції знаходиться безпосередньо перед точкою інстанції шаблону, що додається. В іншому випадку точці обґрунтування такої спеціалізації негайно передує декларація про область імен чи визначення, яке стосується спеціалізації.

Оскільки Cявно неповна на той момент, поведінка інстанції std::is_default_constructible<C>не визначена. Однак див. Основне питання 287 , яке змінило б це правило.


Насправді це пов'язано з NSDMI.

  • NSDMI є дивними, оскільки вони отримують відкладений синтаксичний аналіз, або, звичайно кажучи, вони є "контекстом повного класу".
  • Таким чином, це = 0в принципі могло б посилатися на речі, які Bще не задекларовані, тому реалізація насправді не може спробувати розібрати її, поки не закінчиться B.
  • Завершення класу потребує неявного оголошення спеціальних функцій-членів, зокрема конструктора за замовчуванням, оскільки Cне має оголошеного конструктора.
  • Частини цієї декларації (constexpr-ness, noexcept-ness) залежать від властивостей NSDMI.
  • Таким чином, якщо компілятор не може розібрати NSDMI, він не може завершити клас.
  • Як результат, в момент, коли він створює копію A<C>, він вважає, що Cце неповно.

Вся ця область, що стосується регіонів із затримкою, проаналізованими, надзвичайно визначена, із супутніми розбіжностями щодо впровадження. Це може зайняти деякий час, перш ніж воно очиститься.


0

Не визначена поведінка це:

Якщо примірник вищезазначеного шаблону безпосередньо чи опосередковано залежить від неповного типу, і це опис може дати інший результат, якби цей тип був гіпотетично завершений, поведінка не визначена.


7
Чому C неповний?
interjay

1
@interjay, Cзавершено, але Bце не так. І B::Cопосередковано залежить від цього B.
Evg

1
@Evg Текст "Залежить прямо чи опосередковано" з'являється лише на cppreference.com. Стандарт просто говорить, що тип T повинен бути повним.
interjay


2
@interjay Я написав більшість цих формулювань. Ми намагаємось сказати, що 1) якщо ви індексуєте ознаку таким чином, що може спровокувати порушення ODR пізніше, коли завершиться якийсь неповний тип, це не визначено; і 2) це не визначено, навіть якщо ви насправді не викликаєте порушення ODR у вашій програмі, так що стандартні реалізації бібліотеки можуть вибрати для діагностики, що в точці використовується ознака, якщо вони цього хочуть. Якщо Cв шаблоні конструктора за замовчуванням є якийсь дивний SFINAE, який може змінювати відповіді, якщо Bце завершено інакше, тоді, звичайно, від цього залежить ознака.
ТК
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.