为什么我的类不可默认构造?

Nic*_*las 29 c++ nsdmi

我有这些课程:

#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;
}
Run Code Online (Sandbox Code Playgroud)

编译时,a_m不是默认可构造的,而是a可构造的。

更改C为:

struct C {
      int i;
   };
Run Code Online (Sandbox Code Playgroud)

一切安好。

使用 Clang 9.0.0 测试。

T.C*_*.C. 10

标准文本和评论中指出的几个主要实现都不允许这样做,但出于完全不相关的原因。

首先,“by the book”原因: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是不完整的。

整个处理延迟解析区域的区域都没有详细说明,伴随着实现的分歧。可能需要一段时间才能清理干净。