所有版本的GCC都在使用定义中具有默认类型的模板

Gre*_*ape 41 c++ gcc templates c++11

我浪费了无数个小时来确定gcc的问题.我想用另一个编译器来测试我们的代码库,以寻找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;
}
Run Code Online (Sandbox Code Playgroud)

忽略1std::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上的实例.

Gre*_*ape 34

标准说的是什么([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;
Run Code Online (Sandbox Code Playgroud)

- 结束例子]

所以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;
}
Run Code Online (Sandbox Code Playgroud)

godbolt.org上的实例


可能是由于推断出非默认参数的性质.在函数模板中,它们是从函数参数中扣除的.在类模板中,我们必须明确指定它们.

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