Reputation: 2250
I discovered different compilers behavior when trying to specialize a template. Here is the minimum code that illustrates the problem:
#include <iostream>
template<typename A, typename B>
struct foo {
static const bool value = false;
};
template<typename B>
struct foo<int, B> {
static const bool value = !foo<B, B>::value;
};
int main() {
std::cout << foo<int, int>::value << std::endl;
return 0;
}
I have general template with two parameters and specialized template for first int
type parameter. When using the g++ compiler I get
main.cpp: In instantiation of 'const bool foo<int, int>::value':
main.cpp:10:30: recursively required from 'const bool foo<int, int>::value'
main.cpp:10:30: required from 'const bool foo<int, int>::value'
main.cpp:14:32: required from here
main.cpp:10:30: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
static const bool value = !foo<B, B>::value;
since compiler used value
from specialized version and infinite recursion is obtained. I get
Error C2131 expression did not evaluate to a constant
Error C2065 'value': undeclared identifier
for MSVC. But with clang or zapcc, the code compiles without errors. What is the reason? What is the correct behavior in this case according to the standard or is the behavior undefined?
Upvotes: 2
Views: 98
Reputation: 131546
I am not a language lawyer, but this should not compile. You're trying to initialize a value with itself at compile-time. I would guess this is a clang bug. Actually, I don't see why GCC should be following a recursion path like that if it hits a cycle.
By the way, MSVC tells you (GodBolt):
<source>(14): note: see reference to class template instantiation 'foo<int,int>' being compiled
which is the right catch. For once, MSVC outshines the other compilers... :-P
edit: @aschepler notes that we get (GodBolt) the same behavior even without templates:
struct bar { static const bool value = !bar::value; };
another edit: It seems that until a few years ago this was sort-of valid code, due to "language lawyer sophistry". You see, the standard used to say (Section 6.6.2 [basic.start.static] paragraph 2):
Variables with static storage duration ... shall be zero-initialized... before any other initialization takes place.
and clang took that to mean that first you zero-initialize everything, then you consider the static-duration initializations - which means bar::value
is implicitly decltype(bar::value) { 0 }
before its own explicit initialization. This was changed following Defect Report 2026.
The credit for pointing this out goes to Richard Smith.
Upvotes: 3