模板专门化的不同编译器行为

时间:2019-04-03 22:29:36

标签: c++ gcc visual-c++ clang template-specialization

在尝试专门化模板时,我发现了不同的编译器行为。这是说明问题的最小代码:

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

我有带有两个参数的通用模板和第一个int类型参数的专用模板。使用g++编译器时,我得到

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;

因为编译器从特殊版本使用了value并获得了无限递归。我得到

Error   C2131   expression did not evaluate to a constant
Error   C2065   'value': undeclared identifier  

用于MSVC。但是使用clang或zapcc,代码可以编译而不会出错。是什么原因?在这种情况下,根据标准,正确的行为是什么?行为是否未定义?

1 个答案:

答案 0 :(得分:3)

我不是语言律师,但应该进行编译。您正在尝试在编译时使用其自身来初始化值。我猜这是一个clang错误。实际上,我不明白为什么GCC遇到循环时为什么要遵循这样的递归路径。

顺便说一句,MSVC tells you(GodBolt):

<source>(14): note: see reference to class template instantiation 'foo<int,int>' being compiled

这是正确的选择。 MSVC一次胜过其他编译器... :-P

编辑:@aschepler指出,即使没有模板,我们也get(GodBolt)具有相同的行为:

struct bar { static const bool value = !bar::value; };

另一项编辑:直到几年前,由于“语言律师的诡计”,这似乎是一种有效的代码。您会看到,曾经说过的标准(第6.6.2节[basic.start.static]第2段):

  

具有静态存储期限的变量...应在其他初始化之前进行零初始化...

和clang认为这意味着您首先对所有内容进行零初始化,然后考虑静态持续时间初始化-这意味着bar::value在其自己的显式初始化之前是隐式decltype(bar::value) { 0 }。这是changed following Defect Report 2026

指出这一点的功劳归于Richard Smith