static_assert依赖于非类型模板参数(gcc和clang上的不同行为)

时间:2015-05-06 13:52:17

标签: c++ templates language-lawyer c++14 dependent-name

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

2 个答案:

答案 0 :(得分:14)

两个编译器都是正确的。来自[temp.res] / 8:

  

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

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

如果您只想允许42,则只需不要定义常规模板:

template <int > struct Hitchhiker;
template <> struct Hitchhiker<42> {};

答案 1 :(得分:14)

@TartainLlama发现的行情

  

如果由于不依赖于模板参数的构造而在其定义之后立即对模板进行假设实例化,那么该程序就会形成错误;无需诊断。

N4296 [temp.res] / 8

这在定义主模板后立即适用(其中包含static_assert的模板)。因此,后来的专业化(对于42)不能被考虑,因为它还不存在。

下一个问题是static_assert( sizeof(answer) != sizeof(answer), 取决于answer上的。从语义上来说,它没有,从语法上来说,它是标准的:

  

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

N4296 [temp.dep] / 1

构造sizeof(answer) != sizeof(answer)与一个实例不同。所以这样的构造不依赖模板参数。这意味着整个static_assert不依赖于模板参数。

因此,您的程序形成不良,无需诊断。发出任意诊断(例如static_assert失败)是有效的编译器行为。缺少问题是有效的编译器行为。从标准形成的,没有诊断要求的程序编译的程序的行为不是由标准定义的:它是未定义的行为。允许鼻腔恶魔。

花哨的尝试(如sizeof(int[answer])!=sizeof(int[answer])可能会取悦当前的 god 编译器,但不会使您的程序更加完善。

你可以提出一个案例,编译器不太可能抓住你,但不管编译器是否有能力抓住它,那么形式错误仍然存​​在。作为一般规则,C ++希望让自己(及其编译器)自由地“在实例化之前”找到无效的模板代码;这意味着模板代码必须生成可能合法的代码。

您可能希望=delete之类的内容附加消息。