Dmytro Dadyka
Dmytro Dadyka

Reputation: 2250

Different compilers behavior for template specialization

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

Answers (1)

einpoklum
einpoklum

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

Related Questions