25
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
bolov
  • 58,757
  • 13
  • 108
  • 182
  • GCC seems correct, as a `static_assert` declaration is considered a class member, and should come into being only when the class is instantiated. By the way, you could just leave the primary template a declaration without a definition. – Lingxi May 06 '15 at 14:04
  • @Lingxi `should come into being only when the class is instantiated` I don't think that it is true: E.g. `static_assert(sizeof(int) != sizeof(int), "some error");` will generate error when member of a template, even if the template is never instantiated. – bolov May 06 '15 at 14:09
  • @MarcoA. It is not a duplicate because this `static_assert` is dependent on a template parameter – bolov May 06 '15 at 14:26
  • 1
    Possibly related: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53638 – Marco A. May 06 '15 at 15:17
  • Note: if all you wish is for `Hitchhiker` not to be instantiated with random parameters, you can just *declare* the template as `template struct Hitchhiker;`. The diagnosis is not customisable, then, but it would work identically on both Clang and gcc. – Matthieu M. May 06 '15 at 18:16

2 Answers2

15

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> {};
Barry
  • 247,587
  • 26
  • 487
  • 819
  • 4
    But the template does have a valid specialization. – Lingxi May 06 '15 at 14:15
  • 3
    there is a valid specialization of the template. – bolov May 06 '15 at 14:17
  • 1
    No there isn't. There is no `answer` for which `sizeof(answer) != sizeof(answer)`. – Barry May 06 '15 at 14:20
  • 3
    Your are talking about the primary template which is irrelevant to the explicit specialization `Hitchhiker<42>`. – Lingxi May 06 '15 at 14:22
  • the specialization does not have the `static_assert` member so it is a valid specialization – bolov May 06 '15 at 14:27
  • 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. – 0x499602D2 May 06 '15 at 14:39
  • `Hitchhiker<42>` is not a specialization generated for the template `Hitchhiker`. – Barry May 06 '15 at 14:48
  • @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. – Yakk - Adam Nevraumont May 06 '15 at 14:48
  • 2
    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" – TartanLlama May 06 '15 at 14:48
  • 2
    @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). – Yakk - Adam Nevraumont May 06 '15 at 14:50
  • @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 May 06 '15 at 14:52
  • 2
    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. – TartanLlama May 06 '15 at 14:56
  • @TartanLlama that is looking like an answer. Write it up! – Yakk - Adam Nevraumont May 06 '15 at 15:10
  • 2
    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. – Arne Vogel May 06 '15 at 15:15
  • 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. – Barry May 06 '15 at 15:27
  • @Barry [That was meant to apply this rule to both template definitions and declarations](http://wg21.cmeerw.net/cwg/issue1296), not to limit it. +1, BTW. – T.C. May 08 '15 at 21:06
14

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.

Yakk - Adam Nevraumont
  • 235,777
  • 25
  • 285
  • 465
  • 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. – Matthieu M. May 06 '15 at 17:30
  • @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). – Barry May 06 '15 at 17:47
  • @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? – Yakk - Adam Nevraumont May 06 '15 at 17:53
  • @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? – Matthieu M. May 06 '15 at 18:14
  • 1
    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. – T.C. May 08 '15 at 21:44
  • @Yakk-AdamNevraumont "If it doesn't work out that it isn't dependent, nothing in my logic requires the build to fail" -- But doesn't that mean that your logic is wrong, since it (the compiler) then would have to assume that it *is* dependent, hence not ill-formed, so a compile-time error is required because of the violated static assertion? – philipp2100 Jul 04 '20 at 17:16