问题描述
template <int answer> struct Hitchhiker {
static_assert(sizeof(answer) != sizeof(answer), "Invalid answer");
};
template <> struct Hitchhiker<42> {};
在尝试使用 static_assert
禁用通用模板实例化时,我发现 clang
中的上述代码即使没有实例化模板也会生成断言错误,而 gcc
仅在使用 42
以外的参数实例化 Hitchhiker
时才会生成断言错误.
While trying to disable general template instantiation with static_assert
I discovered that the above code in clang
generates the assert error even when the template is not instantiated, while gcc
generates the assert error only when instantiating Hitchhiker
with a parameter other than 42
.
摆弄我发现这个断言:
template <int answer> struct Hitchhiker {
static_assert(sizeof(int[answer]) != sizeof(int[answer]), "Invalid answer");
};
template <> struct Hitchhiker<42> {};
在两个编译器上的行为相同:断言仅在通用模板实例化时生效.
behaves the same on both compilers: the assert kicks in only when the general template is instantiated.
标准怎么说,哪个编译器是对的?
What does the standard says, which compiler is right?
g++ 4.9.2
clang++ 3.50
推荐答案
@TartainLlama 发现的引用
Quotes found by @TartainLlama
如果紧接其定义的模板的假设实例化由于不依赖于模板参数的构造而格式错误,则程序格式错误;无需诊断.
If a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, the program is ill-formed; no diagnostic is required.
N4296 [temp.res]/8
这在定义主模板后立即适用(其中包含 static_assert
的模板).所以不能考虑后面的专业化(42
),因为它还不存在.
This applies immediately after the primary template is defined (the one with the static_assert
in it). So the later specialization (for 42
) cannot be considered, as it does not exist yet.
下一个问题是 static_assert( sizeof(answer) != sizeof(answer),
depends 是否依赖于 answer
.从语义上讲,它不是,在语法上确实如此,而且在标准方面:
The next question is if static_assert( sizeof(answer) != sizeof(answer),
depends on answer
. Semantically it does not, syntactically it does, and standard-wise:
在模板内部,某些构造的语义可能因实例化而异.这种构造依赖于模板参数.
Inside a template, some constructs have semantics which may differ from one instantiation to another. Such a construct depends on the template parameters.
N4296 [temp.dep]/1
结构 sizeof(answer) != sizeof(answer)
从一个实例到另一个实例没有区别.所以这样的构造不依赖于模板参数.这意味着整个 static_assert
不依赖于模板参数.
The construct sizeof(answer) != sizeof(answer)
does not differ from one instantiation to another. So such a construct does not depend on the template parameters. Which means the entire static_assert
does not depend on the template parameter.
因此您的程序格式错误,无需诊断.发出任意诊断(例如 static_assert
失败)是有效的编译器行为.缺少问题是有效的编译器行为.从格式不正确、不需要诊断的程序编译的程序的行为不是由标准定义的:它是未定义的行为.鼻妖是允许的.
Thus your program is ill formed, no diagnostic required. Issuing an arbitrary diagnostic (such as the static_assert
failing) is valid compiler behavior. Missing the problem is valid compiler behavior. The behavior of a program compiled from an ill formed, no diagnostic required program is not defined by the standard: it is undefined behavior. Nasal demons are permitted.
花哨的尝试(例如 sizeof(int[answer])!=sizeof(int[answer])
可能会取悦当前的 god 编译器,但不会使您的程序形成得更好.
Fancy attempts (like sizeof(int[answer])!=sizeof(int[answer])
may please the current god compiler, but does not make your program more well formed.
您可以提出这样一种情况,即编译器不太可能抓住您,但无论编译器有没有能力抓住您,格式错误仍然存在.作为一般规则,C++ 想让自己(及其编译器)在实例化之前"找到无效模板代码的自由;这意味着模板代码必须生成可能合法的代码.
You could make a case where the compiler is unlikely to be able to catch you at it, but the ill-formed-ness remains regardless of the ability for the compiler to catch you with it. As a general rule, C++ wants to leave itself (and its compilers) freedom to find invalid template code "earlier than instantiation"; this means that template code must produce possibly legal code.
您可能想要诸如 =delete
之类的带有附加消息的内容.
It is possible you want something like =delete
with a message attached.
这篇关于static_assert 依赖于非类型模板参数(gcc 和 clang 上的不同行为)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!