合法使用非跟踪功能模板参数包?

时间:2019-08-01 15:17:27

标签: c++ c++11 variadic-templates default-parameters function-templates

在调试某人讨厌的宏生成的代码时,我看到仅MSVC ++对类似于以下函数模板声明的内容不满意:

template <typename ...Vs, typename>
void foo();

足够公平。我对为什么GCC和Clang会编译它感到困惑。我在上面的声明中添加了foo的定义,现在GCC也产生了编译错误(Clang仍然是内容)。这段代码如下:

template <typename ...Vs, typename>
void foo();

template <typename ...Vs, typename = int>
void foo() {  }

int main(int argc, char *argv[])
{
  foo<char,float>();
  return 0;
}

Clang是对还是错?我注意到,如果删除了声明,则GCC和MSVC ++可以编译。

2 个答案:

答案 0 :(得分:5)

C语是正确的。如果以下参数具有默认参数,则模板parameter pack可能会更早出现。

  

在主类模板中,模板参数包必须是模板参数列表中的最终参数。在函数模板中,模板参数包可能会出现在列表的前面,但前提是可以从函数参数推导出以下所有参数,或者使用默认参数:

template<typename... Ts, typename U> struct Invalid; // Error: Ts.. not at the end

template<typename ...Ts, typename U, typename=void>
void valid(U, Ts...);     // OK: can deduce U
// void valid(Ts..., U);  // Can't be used: Ts... is a non-deduced context in this position

valid(1.0, 1, 2, 3);      // OK: deduces U as double, Ts as {int,int,int}

然后将给定的foo<char,float>();Vs推导为char, float,第二个模板参数将为int

根据标准,[temp.param]/13

  

如果主类模板,主变量模板或别名模板的模板参数是模板参数包,则它应是最后一个模板参数。除非可以从功能模板的参数类型列表([dcl.fct])推导出该模板参数或具有默认参数([temp。扣除])。

关于gcc和msvc的行为,

  

如果删除了声明,GCC和MSVC ++将成功编译。

Gcc和MSVC似乎无法合并出现在声明中的default template arguments,并且应该合并定义。

  

出现在声明和   定义类似于默认函数参数的合并:

template<typename T1, typename T2 = int> class A;
template<typename T1 = int, typename T2> class A;
// the above is the same as the following:
template<typename T1 = int, typename T2 = int> class A;

答案 1 :(得分:1)

不确定,但在我看来g ++是正确的,而clang ++是错误的。

songyuanyao说的对,当“以下参数具有默认参数”时,模板参数不一定位于最后位置。

但是g ++同意这一点,因为如果您在声明中添加默认参数,则将其从定义中删除,

template <typename ...Vs, typename = int>
void foo();

template <typename ...Vs, typename>
void foo() {  }

int main ()
{
  foo<char,float>();
}

两个编译器(也都是g ++)编译都没有问题。

恕我直言,关键是:我们可以在函数定义(仅函数定义;在函数声明之后)中还是仅在函数声明中表达模板默认参数吗?

对我来说还不是很清楚,但是,阅读C ++ 17标准后,我会看到(17.1.9)

  

可以在模板声明中指定默认模板参数。不得在出现在成员类之外的类模板成员的定义的template-parameter-list中指定默认模板参数。默认的模板参数不能在朋友类模板声明中指定。如果好友功能模板声明指定了默认模板参数,则该声明应为定义,并且应为翻译单元中功能模板的唯一声明

因此,肯定会在“声明”而不是“定义”中接受默认模板参数。

我再说一遍:不确定,因为在定义为“出现在该成员类之外的类模板的成员”的情况下被明确排除,而不是在声明后进行泛型定义的情况下。

无论如何,我看不出有人在可以在声明之前的定义中接受默认模板参数。

在10.1.10中,我们有

  

可用的默认模板参数集是通过以默认函数参数相同的方式合并模板的所有先前声明中的默认参数而获得的

再次:“声明”,而不是“定义”。

所以我认为g ++是错误的,因为在声明后的定义中不应接受默认的模板参数。