By bolov

2015-05-06 13:52:16 8 Comments

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


@Barry 2015-05-06 14:12:22

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> {};

@Lingxi 2015-05-06 14:15:11

But the template does have a valid specialization.

@bolov 2015-05-06 14:17:15

there is a valid specialization of the template.

@Barry 2015-05-06 14:20:34

No there isn't. There is no answer for which sizeof(answer) != sizeof(answer).

@Lingxi 2015-05-06 14:22:01

Your are talking about the primary template which is irrelevant to the explicit specialization Hitchhiker<42>.

@bolov 2015-05-06 14:27:22

the specialization does not have the static_assert member so it is a valid specialization

@0x499602D2 2015-05-06 14:39:04

I think Barry is right. No valid specialization can be generated for the primary template, so it is diagnosed at definition time. This is permitted in the standard in some clause I don't have time to find.

@Barry 2015-05-06 14:48:27

Hitchhiker<42> is not a specialization generated for the template Hitchhiker<int >.

@Yakk - Adam Nevraumont 2015-05-06 14:48:47

@0x49 The best tea leaf reading I can make up is "a partial specialization is not a specialization. Each partial specialization is a template, as is the primary template. For a template to have a valid specialization means there is some set of template parameters for which the template is valid. The term 'specialization' here does not refer to 'partial specialization', they are distinct terms. While you think Hitchhiker<42> is a full specialization, it is called a partial specialization in the standard", but it is an argument on very shakey ground.

@TartanLlama 2015-05-06 14:48:55

Another quote from further down which seems applicable: "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"

@Yakk - Adam Nevraumont 2015-05-06 14:50:31

@Tart A problem is, sizeof(answer) != sizeof(answer) is a construct that depends on answer (in a trivial, boring way, but it still depends on it)? Basically false, but false clearly does not depend on answer, while answer != answer does (syntactically).

@TartanLlama 2015-05-06 14:52:53

@Yakk Yeah, I thought that too and you're most likely right. But I could see a valid argument that the expression evaluates at compile time in the same manner regardless of the parameter, therefore the value is independent of the parameter.

@TartanLlama 2015-05-06 14:56:39

From [temp.dep] "Inside a template, some constructs have semantics which may differ from one instantiation to another. Such a construct depends on the template parameters". The semantics here do not differ from one instantiation to another, so you could argue that the construct doesn't depend on the parameter.

@Yakk - Adam Nevraumont 2015-05-06 15:10:30

@TartanLlama that is looking like an answer. Write it up!

@Arne Vogel 2015-05-06 15:15:12

The quoted passage however does not require a non-dependent expression. If a compiler can prove that the template cannot be instantiated, it is free to issue a diagnostic even if the cause is a dependent expression or type. Obviously, a diagnostic cannot be required because of Gödel's incompleteness theorems. There are more complex workarounds for "static_assert(false, ...)" in a template that shouldn't be instantiated (by making a dependent expression), but though they appease clang, they are not sufficient for making the program well-formed.

@Barry 2015-05-06 15:27:57

Interesting that the wording changes for [temp.res]/8. Used to be "If no valid specialization can be generated for a template definition, and that template is not instantiated..." The word definition had been removed.

@T.C. 2015-05-08 21:06:52

@Barry 2015-05-09 02:50:36

@T.C. Makes sense, thanks

@Yakk - Adam Nevraumont 2015-05-06 15:42:11

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.

@Matthieu M. 2015-05-06 17:30:16

I disagree with your interpretation of [temp.dep]/1: I take the may as a giving lee-way to the compiler to avoid actually attempting the question of whether the construct will indeed have different semantics in one instantiation or the other, and instead let the compiler decide that if there is a chance it could have different semantics (it mentions the template parameters), then it is considered as depending. I believe this lee-way is extremely important, as the expressions could be arbitrary convoluted and thus determining whether an instantiation is possible or not be complex.

@Barry 2015-05-06 17:47:40

@MatthieuM. It could be arbitrarily convoluted - that's why it's ill-formed, no diagnostic required. However, I'm not sure the particular passage is relevant here - the "hypothetical instantiation" seems to refer specifically to incomplete types/definitions (see the note).

@Yakk - Adam Nevraumont 2015-05-06 17:53:41

@MatthieuM. The issue in this case is that on one hand we have "the program is ok", and on the other "the program is ill-defined, no diagnostic required". One way to treat "ill formed no diagnostic required" is to compile it into an "ok" program. If the compiler can prove that the sizeof(answer)!=sizeof(answer) is not dependent, it can then evaluate it before instantiation, and find errors and (diagnose) (high QOI) or (engage in arbitrary behavior) (rude QOI). If it doesn't work out that it isn't dependent, nothing in my logic requires the build to fail?

@Matthieu M. 2015-05-06 18:14:35

@Yakk: Oh, I perfectly agree with most of your answer, and the part about compiling or diagnosing being a QOI issue. I had the impression, however, than when you said "So such a construct does not depend on the template parameters" you meant it as the only possible outcome, which does not explain the difference in behavior. Maybe refining this sentence to explain than a compiler may (or not) realize that the construct does not depend on the template parameters would make your point clearer?

@T.C. 2015-05-08 21:44:17

There's a full set of rules to decide whether an expression like sizeof(answer) is type-dependent or value-dependent. Spoiler: It's neither.

Related Questions

Sponsored Content

1 Answered Questions

1 Answered Questions

1 Answered Questions

[SOLVED] Get similar behavior between Clang and GCC for uninstantiated templates?

2 Answered Questions

Sponsored Content