在尝试专门化模板时,我发现了不同的编译器行为。这是说明问题的最小代码:
#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,代码可以编译而不会出错。是什么原因?在这种情况下,根据标准,正确的行为是什么?行为是否未定义?
答案 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。