为什么我的班级不是默认可构造的?

时间:2019-12-09 15:52:15

标签: c++

我有那些课程:

#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测试。

2 个答案:

答案 0 :(得分:8)

该标准的文本和评论中提到的几种主要实现方式均不允许这样做,但是出于完全不相关的原因。

首先,“按书”的原因:A<C>的实例化点根据标准为immediately before the definition of B,而std::is_default_constructible<C>的实例化点紧接在之前

  

对于类模板专业化,如果专业化是   隐式实例化,因为它是从另一个内部引用的   模板专业化,如果专业化所来自的上下文   被引用取决于模板参数,以及   没有在实例化之前实例化专门化   封闭模板,实例化点是立即   在封闭模板的实例化点之前。   否则,这种专业化的实例化点   紧接在名称空间范围声明或定义之前   指的是专业化。

由于C在那时显然是不完整的,因此实例化std::is_default_constructible<C>的行为是不确定的。但是,请参见core issue 287,它将更改此规则。


实际上,这与NSDMI有关。

  • NSDMI很奇怪,因为它们被延迟解析-或按照标准的说法,它们是“完整类上下文”。
  • 因此,= 0原则上可以引用B中尚未声明的内容,因此该实现在完成B后才能真正尝试对其进行解析。 / li>
  • 完成类需要隐式声明特殊成员函数,尤其是默认构造函数,因为C没有声明构造函数。
  • 该声明的部分内容(constexpr-ness,noexcept-ness)取决于NSDMI的属性。
  • 因此,如果编译器无法解析NSDMI,则它将无法完成该类。
  • 结果,在实例化A<C>时,它认为C是不完整的。

整个处理延迟分析区域的区域都严重不足,伴随着实施上的分歧。可能需要一段时间才能清理干净。

答案 1 :(得分:0)

Undefined behavior是:

  

如果以上模板的实例依赖,则直接或   间接地,对于不完整的类型,该实例化可以产生   如果假设该类型已完成,则结果会不同,   行为是不确定的。