У меня есть эти классы:
#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.
C() {}
этим тоже работает.static_assert
in inA
терпит неудачу, но если вы вместо этого по умолчанию создаетеT
внутреннюю частьA
(например, помещаете туда членT t;
), все работает нормально. Несоответствие между тем, чтоconst int x;
недопустимо без инициализатора, просто из-const
за поведения и инициализации встроенных типов и некоторых история)Ответы:
Это запрещено как текстом стандарта, так и несколькими основными реализациями, как отмечено в комментариях, но по совершенно не связанным причинам.
Во-первых, причина «по книге»: точка инстанцирования
A<C>
, согласно стандарту, находится непосредственно перед определениемB
, а точка инстанцированияstd::is_default_constructible<C>
находится непосредственно перед этим:Поскольку
C
в этот момент он явно неполон, поведение создания экземпляровstd::is_default_constructible<C>
не определено. Однако, смотрите основную проблему 287 , которая изменит это правило.На самом деле это связано с NSDMI.
= 0
в принципе это может относиться к вещам, которыеB
еще не объявлены, поэтому реализация не может попытаться проанализировать их до тех пор, пока они не закончатсяB
.C
конструктор не объявлен.A<C>
, он думает, чтоC
он неполон.Вся эта область, имеющая дело с областями с анализом с задержкой, крайне не указана, что сопровождается расхождением в реализации. Это может занять некоторое время, прежде чем он будет очищен.
источник
Неопределенное поведение это:
источник
C
завершено, ноB
нет. ИB::C
косвенно зависит отB
.C
него есть шаблон конструктора по умолчанию с каким-то странным SFINAE, который может изменить ответы, еслиB
он выполнен по-другому, то, конечно, от этого зависит черта.