Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function overloading: empty parameter list vs parameter pack

template <typename T>
void call(T) {  //#1
    std::cout << "1" << std::endl;
}

template <typename T, typename...Args>
void call(T, Args...) {  //#2
    std::cout << "2" << std::endl;
}

When I call the function like this

call(10);

GCC, Clang, and MSVC all use #1.

However, the partial ordering rule in the standard says:

If the parameter-declaration corresponding to Pi is a function parameter pack, then the type of its declarator-id is compared with each remaining parameter type in the parameter-type-list of A. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. During partial ordering, if Ai was originally a function parameter pack:

  • (10.1) if P does not contain a function parameter type corresponding to Ai then Ai is ignored;

  • (10.2) otherwise, if Pi is not a function parameter pack, template argument deduction fails.

When we deduce #1 from #2, with T, Args... as A, T as P, P does not contain a template argument corresponding to Args.... Args... is ignored, so #1 can be deduced from #2 successfully.

Then the deducing #2 from #1, with T as A and T, Args... as P, also successfully results in T = T, Args... = {}.

Therefore, according to the partial ordering rules, when call call(10), the compiler should be giving the ambiguous error, but in fact all compiler called #1, why is this?

like image 234
xmh0511 Avatar asked Oct 15 '22 11:10

xmh0511


1 Answers

The compilers are correct. #1 is more specialized than #2.


The rule for partial ordering template parameter packs is specified in [temp.deduct.partial]/8:

Using the resulting types P and A, the deduction is then done as described in [temp.deduct.type]. If P is a function parameter pack, the type A of each remaining parameter type of the argument template is compared with the type P of the declarator-id of the function parameter pack. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. Similarly, if A was transformed from a function parameter pack, it is compared with each remaining parameter type of the parameter template. If deduction succeeds for a given type, the type from the argument template is considered to be at least as specialized as the type from the parameter template.

When the parameter template is #1 and the argument template is #2, the declarator-id Args (which is a type) from #2 is compared with each of the remaining parameters in #1 (there's none). Therefore, #2 is at least as specialized as #1.

When the parameter template is #2 and the argument template is #1, the declarator-id Args (which is a type) from #2 is compared with each of the remaining parameters in #1 (there's none). Therefore, #1 is at least as specialized as #2.

Seems ambiguous, right? Now we have the tie breaker, [temp.deduct.partial]/11:

If, after considering the above, function template F is at least as specialized as function template G and vice-versa, and if G has a trailing parameter pack for which F does not have a corresponding parameter, and if F does not have a trailing parameter pack, then F is more specialized than G.

Apparently, #1 is more specialized than #2.

like image 164
L. F. Avatar answered Nov 15 '22 09:11

L. F.