Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

static_assert dependent on non-type template parameter (different behavior on gcc and clang)

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
like image 870
bolov Avatar asked May 06 '15 13:05

bolov


2 Answers

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> {};
like image 50
Barry Avatar answered Nov 20 '22 20:11

Barry


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.

like image 42
Yakk - Adam Nevraumont Avatar answered Nov 20 '22 21:11

Yakk - Adam Nevraumont