I've come across some interesting variadic template function behaviour. Can anyone point out the relevant rules in the standard which define this?
GCC, ICC and MSVC compile the following code successfully (Clang doesn't, but I understand that this is due to compiler bugs).
template<class A, class... Bs, class C>
void foo(A, Bs..., C) { }
int main()
{
foo<int, int, int, int>(1, 2, 3, 4, 5);
}
In this call to foo
, template arguments are provided for A
and Bs
, then C
is deduced to be int
.
However, if we simply flip the last two template parameters:
template<class A, class C, class... Bs>
void foo(A, Bs..., C) { }
Then all three compilers throw errors. Here is the one from 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);
^
To make things more interesting, calling with only four arguments is invalid for the first foo
, and valid for the second.
It seems that in the first version of foo
, C
must be deduced, whereas in the second, C
must be explicitly supplied.
What rules in the standard define this behaviour?
As is often the case, the answer came to me a few hours after I posted the question.
Consider the two versions of 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) { }
and the following call (assuming foo
is foo1
or foo2
):
foo<int,int,int,int>(1,2,3,4,5);
In the case of foo1
, the template parameters are picked as follows:
A = int (explicitly provided)
Bs = {int,int,int} (explicitly provided)
C = int (deduced)
But in the case of foo2
they look like this:
A = int (explicitly provided)
C = int (explicitly provided)
Bs = {int,int} (explicitly provided)
Bs
is in a non-deduced context ([temp.deduct.type]/5.7
), so any further function arguments can not be used to extend the pack. As such, foo2
must have all it's template arguments explicitly provided.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With