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

时间:2017-05-16 05:28:31

标签: c++ c++11 templates gcc

我浪费了无数个小时来确定 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;
}

忽略1中的std::enable_if<1>。显然,它只是一个不变的价值,只是为了在无关紧要时不会使事情复杂化。

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

问题是,谁是对的,谁在这里错了? gcc 是否符合标准?

显然这不是一个关键问题。我可以轻松地将std::enable_if移到声明中。唯一的受害者就是美学。但是能够在实现中隐藏一个难看的100个字符长的std::enable_if段代码,这对于库函数的用户来说并不是立即相关的。

godbolt.org上的实例。

1 个答案:

答案 0 :(得分:34)

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

  

可用于a的默认模板参数集   模板声明或定义是通过合并默认值获得的   定义中的参数(如果在范围内)和中的所有声明   范围与默认函数参数相同(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

上的实例

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

无论如何,我创建了一个bug report