我有那些课程:
#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测试。
答案 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有关。
= 0
原则上可以引用B
中尚未声明的内容,因此该实现在完成B
后才能真正尝试对其进行解析。 / li>
C
没有声明构造函数。A<C>
时,它认为C
是不完整的。整个处理延迟分析区域的区域都严重不足,伴随着实施上的分歧。可能需要一段时间才能清理干净。
答案 1 :(得分:0)
如果以上模板的实例依赖,则直接或 间接地,对于不完整的类型,该实例化可以产生 如果假设该类型已完成,则结果会不同, 行为是不确定的。