Use an even simpler example:
template<class T> void foo(T, T); foo(42, {});
A function call has two arguments:
int prvalue expression (integer literal)- copied-init-list
{}
The latter, {} , may be part of the list of expressions, but this is not the expression itself. A list of expressions is defined as a list of initializers. In files with extended lists, no type is specified.
The calculation of the type of the template is performed for each parameter of the function separately [temp.deduct.type] / 2. [temp.deduct.call] / 1 indicates type inference for function parameter P :
If removing links and cv-qualifiers from P gives std::initializer_list< P ' > for some P', and the argument is a list of initializers, then for each element the list of initializers is deduced, taking P 'as the parameter of the function template type and initializer in as an argument. Otherwise, the argument of the list of initializers causes the parameter to be considered an inactive context. [emphasis mine]
So in the call foo(42, {}); T will not be inferred from the second argument {} . However, T can be inferred from the first argument.
In the general case, we can deduce T from several parameters of the function. In this case, the inferred types must exactly match [temp.deduct.type] / 2. There is no problem if the type is inferred from only one parameter of the function, but is used in another place (in another parameter of the function, which is in an undetectable context, in the return type, etc.). A type deduction may fail, for example. when the template parameter cannot be inferred from any function parameter and is not set explicitly.
After subtraction, T will be replaced by int , creating a function signature similar to:
void foo<int>(int, int);
This function can be called with two arguments 42 and {} . The latter will initialize the list of copies, leading to the initialization of the value of the second parameter.