Search code examples
c++templatesoverloadingoverload-resolutionparameter-pack

Parameter pack in overload resolution


Consider the following code:

template<class T>
void f(const T& a, const T&b) { std::cout << "#1"; }

template<class ...T>
void f(T&& ...a) { std::cout << "#2"; }
 
int main() { f(0, 1); } // prints #2

I wonder why #2 is considered more specialized during overload resolution. The following are my reasoning for #1 being more specialized.

Following the rules for determining the partial ordering of function templates, we first perform deduction for transformed #1 against original template #2. From standard,

To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs (13.7.4) thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template.

Suppose the unique synthesized type for transformed #1 is for T is U. Then during template argument deduction, for both arguments a and b, we have P = T&& and A = const U&. Following the deduction rules, we get T = const U& for both arguments. The deduction succeeds, so #1 is at least as specialized as #2.

Now we perform deduction for transformed #2 against original template #1. According to this paragraph:

If the parameter-declaration corresponding to P_i 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 A_i was originally a function parameter pack:

  • if P does not contain a function parameter type corresponding to A_i then A_i is ignored;
  • otherwise, if P_i is not a function parameter pack, template argument deduction fails.

In our case, our first argument A_1 was originally a function parameter pack, but P_1 is not a parameter pack. So template argument deduction fails, and #2 is not at least as specialized as #1. So #1 is more specialized.

Where does my reasoning break down?


Solution

  • I wonder why #2 is considered more specialized during overload resolution.

    That consideration would happen later.

    The selection is already be done from "previous" criteria:

    From Best_viable_function:

    1. there is at least one argument of F1 whose implicit conversion is better than the corresponding implicit conversion for that argument of F2

    And then from Ranking_of_implicit_conversion_sequences

    c) or, if not that, both S1 and S2 are binding to a reference parameter to something other than the implicit object parameter of a ref-qualified member function, and S1 binds an rvalue reference to an rvalue while S2 binds an lvalue reference to an rvalue

    e) or, if not that, both S1 and S2 are binding to a reference parameters only different in top-level cv-qualification, and S1's type is less cv-qualified than S2's.