我有以下程序:
constexpr int flag (int);
template<class Tag>
struct writer {
friend constexpr int flag (Tag) {
return 0;
}
};
template<bool B, class Tag = int>
struct dependent_writer : writer<Tag> { };
template<
bool B = noexcept (flag (0)),
int = sizeof (dependent_writer<B>)
>
constexpr bool f () {
return B;
}
int main () {
constexpr bool a = f();
constexpr bool b = f();
static_assert( !a, "was not instantiated" );
static_assert( !b, "was not instantiated" ); // here's the difference
}
有趣的是,这个程序用C ++ 11,C ++ 14和C ++ 17模式编译gcc 5.2和clang 3.6。但是,使用gcc 4.7,4.8和4.9会发生以下错误:
main.cpp: In function 'int main()':
main.cpp:26:7: error: static assertion failed: was not instantiated
static_assert( !b, "was not instantiated" ); // here's the difference
^
似乎在旧的编译器中,模板函数f()
的默认模板参数是在函数调用站点决定的。第一次调用f()
时,模板类writer<int>
最终被实例化,它定义了函数外部函数flag()
。这会更改noexcept(flag(0))
的值,因为编译器现在可以通过检查告诉该函数是noexcept,因为它找到了一个定义,第二次调用f()
。因此,在第二次调用f()
时,找到的模板参数是不同的。
另一方面,较新的编译器似乎在声明模板函数f()
时决定默认模板参数,因此结果两次都相同。
这种以前的行为(gcc 4.7到4.9)对我来说有点奇怪。我想知道,如果这种行为是标准的符合,如果不符合,标准的哪一部分是矛盾的。
注意:问题Compile time template instantiation check有一个可接受的答案,它依赖于旧编译器的描述行为。因此,回答这个问题将有助于找出接受的解决方案是否正确。