令人困惑的非尾随参数包行为

Tar*_*ama 10 c++ templates language-lawyer

我遇到了一些有趣的可变参数模板函数行为.任何人都能指出标准中定义此规则的相关规则吗?

GCC,ICCMSVC成功编译了以下代码(Clang没有,但我知道这是由于编译器错误).

template<class A, class... Bs, class C>
void foo(A, Bs..., C) { }

int main()
{
    foo<int, int, int, int>(1, 2, 3, 4, 5);
}
Run Code Online (Sandbox Code Playgroud)

在此呼吁foo,提供了模板参数为ABs,然后C推断为int.

但是,如果我们只是翻转最后两个模板参数:

template<class A, class C, class... Bs>
void foo(A, Bs..., C) { }
Run Code Online (Sandbox Code Playgroud)

然后所有 三个 编译器都会抛出错误.这是GCC的一个:

main.cpp: In function 'int main()':
main.cpp:8:42: error: no matching function for call to 'foo(int, int, int, int, int)'
     foo<int, int, int, int>(1, 2, 3, 4, 5);
                                          ^
main.cpp:4:6: note: candidate: template<class A, class C, class ... Bs> void foo(A, Bs ..., C)
 void foo(A, Bs..., C) { }
      ^~~
main.cpp:4:6: note:   template argument deduction/substitution failed:
main.cpp:8:42: note:   candidate expects 4 arguments, 5 provided
     foo<int, int, int, int>(1, 2, 3, 4, 5);
                                      ^
Run Code Online (Sandbox Code Playgroud)

为了让事情更有趣,只用四个参数调用是无效的第一foo,并有效第二.

看来,在第一个版本foo,C 必须被推断,而在第二,C 必须明确提供.

标准中的哪些规则定义了这种行为?

Tar*_*ama 2

通常情况下,我在发布问题几个小时后就得到了答案。

考虑以下两个版本foo

template<class A, class... Bs, class C>
void foo1(A, Bs..., C) { }

template<class A, class C, class... Bs>
void foo2(A, Bs..., C) { }
Run Code Online (Sandbox Code Playgroud)

和以下调用(假设foofoo1or foo2):

foo<int,int,int,int>(1,2,3,4,5);
Run Code Online (Sandbox Code Playgroud)

在 的情况下foo1,模板参数的选择如下:

A = int (explicitly provided)
Bs = {int,int,int} (explicitly provided)
C = int (deduced)
Run Code Online (Sandbox Code Playgroud)

foo2但在它们看起来像这样的情况下:

A = int (explicitly provided)
C = int (explicitly provided)
Bs = {int,int} (explicitly provided)
Run Code Online (Sandbox Code Playgroud)

Bs位于非推导上下文 ( [temp.deduct.type]/5.7) 中,因此任何其他函数参数都不能用于扩展包。因此,foo2必须显式提供所有模板参数。