Which is the most specialized function template in the context of taking the address?

Viewed 125

Consider this example

#include <iostream>
template<class T>
void fun(T&){  //#1
    std::cout<<"selected\n";
}

template<class T>
void fun(T&&){}  //#2

int main() {
    void(*ptr)(int&) = &fun;  //#3
}

Both GCC and Clang report an error with the diagnosis "ambiguous". According to [temp.deduct.funcaddr#1], such two function templates are all viable in #3. Hence, [over.over#5] needs to apply here

Any given function template specialization F1 is eliminated if the set contains a second function template specialization whose function template is more specialized than the function template of F1 according to the partial ordering rules of [temp.func.order].

To judge which is more specialized between #1 and #2, [temp.deduct.partial#3.3] applies to them

The types used to determine the ordering depend on the context in which the partial ordering is done:

  • [...]
  • In other contexts the function template's function type is used.

Hence, the P and A used to participate in partial ordering should be the function type of that two function templates, respectively. Take the function type of #2 as P and the function type of #1 as A. Deduce P from A is successful as per [temp.deduct.type#10]

If P and A are function types that originated from deduction when taking the address of a function template ([temp.deduct.funcaddr]) or when deducing template arguments from a function declaration ([temp.deduct.decl]) and Pi and Ai are parameters of the top-level parameter-type-list of P and A, respectively, Pi is adjusted if it is a forwarding reference ([temp.deduct.call]) and Ai is an lvalue reference, in which case the type of Pi is changed to be the template parameter type (i.e., T&& is changed to simply T).

Conversely, Since we cannot deduce T& from T&&, hence the function template at #1 is more specialized than the function template at #2. Hence the specialization of #2 should be eliminated from the set. Ultimately, the set only contains exactly one specialization of #1. It should be unambiguous here. Why do GCC and Clang say the taking address is ambiguous?

1 Answers

GCC and Clang are wrong to yield and ambiguity in overload resolution, as per your own analysis.

This arguably relates to CWG 1164, albeit not being in the context of a function call, the intent should arguably be similar as for the case of functions calls as per CWG 1164 [emphasis mine]:

1164. Partial ordering of f(T&) and f(T&&)

Section: 13.10.3.2 [temp.deduct.call] Status: C++11 Submitter: US Date: 2010-08-03

[Voted into the WP at the November, 2010 meeting.]

N3092 comment US 77 The following example is ambiguous:

template<typename T> int f(T&);
template<typename T> int f(T&&);
int i;
int j = f(i);

Because of the special deduction rule for lvalues passed to rvalue-reference parameters, deduction produces f(int&) for both templates, and they are indistinguishable.

Because f(T&) accepts a strict subset of the things that f(T&&) does, it should be considered more specialized by the partial ordering rules.

Proposed resolution (August, 2010): [...]

Your own analysis reaches an overload result which is aligned with CWG 1164 but for another context, which was never lifted in CWG 1164 and which is (arguably) less common.


We may note that both GCC and Clangs marks the resolution of CWG 1164 as ?/Unknown:
- GCC: C++ Defect Report Support in GCC
- C++ Defect Report Support in Clang
So possibly it has only been partially implemented (OP's example is more of a corner use case than that of the CWG 1164).

Related