25
template <int answer> struct Hitchhiker {
  static_assert(sizeof(answer) != sizeof(answer), "Invalid answer");
};

template <> struct Hitchhiker<42> {};

在尝试禁用通用模板实例化时,static_assert我发现clang即使模板未实例化,上述代码也会生成断言错误,而gcc仅在Hitchhiker使用42.

摆弄我发现这个断言:

template <int answer> struct Hitchhiker {
  static_assert(sizeof(int[answer]) != sizeof(int[answer]), "Invalid answer");
};

template <> struct Hitchhiker<42> {};

在两个编译器上的行为相同:断言仅在通用模板被实例化时才生效。

标准是怎么说的,哪个编译器是正确的?

g++ 4.9.2
clang++ 3.50
4

2 回答 2

15

两个编译器都是正确的。从 [temp.res]/8 开始:

如果无法为模板生成有效的特化,并且该模板未实例化,则模板格式错误,无需诊断。

不存在可以从主模板生成的有效Hitchhiker特化,因此它是格式错误的,不需要诊断。无论如何,clang 选择发出诊断。

如果您只想允许42,则根本不定义通用模板:

template <int > struct Hitchhiker;
template <> struct Hitchhiker<42> {};
于 2015-05-06T14:12:22.877 回答
14

@TartainLlama 找到的名言

如果紧跟其定义的模板的假设实例由于不依赖于模板参数的构造而格式错误,则程序是格式错误的;不需要诊断。

N4296 [温度分辨率]/8

这在定义主模板(其中包含的模板)后立即应用static_assert。所以42不能考虑后面的专业化(for),因为它还不存在。

下一个问题是是否static_assert( sizeof(answer) != sizeof(answer), 取决于. answer语义上它没有,语法上它有,并且在标准方面:

在模板内部,一些结构的语义可能因一个实例化而异于另一个实例化。这样的构造取决于模板参数。

N4296 [温度.dep]/1

构造sizeof(answer) != sizeof(answer)在一个实例与另一个实例之间没有区别。所以这样的构造不依赖于模板参数。这意味着整个static_assert不依赖于模板参数。

因此,您的程序格式不正确,不需要诊断。发出任意诊断(例如static_assert失败)是有效的编译器行为。缺少问题是有效的编译器行为。从格式不正确、不需要诊断的程序编译的程序的行为没有被标准定义:它是未定义的行为。鼻恶魔是允许的。

花哨的尝试(sizeof(int[answer])!=sizeof(int[answer])可能会取悦当前的上帝编译器,但不会使您的程序格式更好。

您可以提出编译器不太可能捕获您的情况,但是无论编译器是否能够捕获您,格式错误仍然存​​在。作为一般规则,C++ 希望让自己(及其编译器)自由地“在实例化之前”找到无效的模板代码;这意味着模板代码必须产生可能合法的代码。

您可能想要=delete带有附加消息的东西。

于 2015-05-06T15:42:11.660 回答