template <int answer> struct Hitchhiker {
static_assert(sizeof(answer) != sizeof(answer), "Invalid answer");
};
template <> struct Hitchhiker<42> {};
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
.
Fiddling around I found that this assert:
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
Both compilers are correct. From [temp.res]/8:
If no valid specialization can be generated for a template, and that template is not instantiated, the template is ill-formed, no diagnostic required.
There does not exist a valid specialization that can be generated from the primary template Hitchhiker
, so it is ill-formed, no diagnostic required. clang chooses to issue a diagnostic anyway.
If you only want to allow 42
, then simply don't define the general template:
template <int > struct Hitchhiker;
template <> struct Hitchhiker<42> {};
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
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.
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
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.
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.
Fancy attempts (like sizeof(int[answer])!=sizeof(int[answer])
may please the current god compiler, but does not make your program more well formed.
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.
It is possible you want something like =delete
with a message attached.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With