GCC的所有版本都与在定义中具有默认types的模板拼争

我浪费了无数个小时来查明gcc的问题。 我想用另外一个编译器来testing我们的代码库,以寻找更多的Clang可能错过的警告。 我感到震惊的是,由于模板论证扣除的失败,几乎有一半的项目停止编译。 在这里,我试图把我的情况弄到最简单的一段代码。

#include <type_traits> struct Foo { }; // This is a template function declaration, where second template argument declared without a default template <typename T, typename> void foo(const Foo & foo, T t); // This is a template function definition; second template argument now has a default declared template <typename T, typename = typename std::enable_if<1>::type> void foo(const Foo & foo, T t) { } int main(int argc, char ** argv) { foo(Foo{}, 1); return 0; } 

忽略std::enable_if<1> 。 显然,只要没有问题,不要使事情复杂化,这是一个不变的价值。

这段代码用clang (3.4到4.0), icc (16,17), Visual C ++ (19.00.23506)编译[1] 。 基本上,我找不到任何其他的c + + 11编译器,除了gcc (4.8到7.1),不编译这段代码。

问题是,谁是对的,谁在这里错了? gcc是否按照标准行事?

显然这不是一个关键问题。 我可以很容易地将std::enable_if移动到声明中。 唯一的受害者将是美学。 但是能够隐藏一个丑陋的100个字符长的std::enable_if代码片段,这对于库函数的用户来说并不直接相关。


在godbolt.org上的实例 。

标准所说的( [1]第350页):

可以与模板声明或定义一起使用的默认模板参数集合是通过合并来自定义的默认参数(如果在作用域中)和作用域中的所有声明以默认函数参数(8.3.6)相同的方式合并获得的。 [例如:

 template<class T1, class T2 = int> class A; template<class T1 = int, class T2> class A; is equivalent to template<class T1 = int, class T2 = int> class A; 

– 结束示例]

所以GCC在这里是错误的。 它忽略声明中的默认模板参数。

不是所有的声明,只是函数模板声明。 类模板声明是可以的:

 #include <type_traits> template <typename T, typename> struct Foo; template <typename T, typename = typename std::enable_if<1>::type> struct Foo { T t; }; int main() { Foo<int> foo; return 0; } 

在godbolt.org上的实例


可能是由于如何推导出非默认参数的本质。 在函数模板中,它们从函数参数中被扣除。 在类模板中,我们必须明确地指定它们。

无论如何,我已经创build了一个错误报告 。