我浪费了无数个小时来确定 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上的实例。
答案 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;
}
上的实例
可能是由于推断出非默认参数的性质。在函数模板中,它们是从函数参数中扣除的。在类模板中,我们必须明确指定它们。
无论如何,我创建了一个bug report。