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?
The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.
Template in C++is a feature. We write code once and use it for any data type including user defined data types. For example, sort() can be written and used to sort any data type items. A class stack can be created that can be used as a stack of any data type.
Overloading it by either a different function template or a non-template function is arguably superior because its handling is more intuitive and it's overall more powerful (effectively by overloading the template, you have a partial specialization of the template, even though technically it's called partial ordering).
Defining a Function Template A function template starts with the keyword template followed by template parameter(s) inside <> which is followed by the function definition. In the above code, T is a template argument that accepts different data types ( int , float , etc.), and typename is a keyword.
FUNCTION TEMPLATE:- Function templates allow the programmer to write a generic function which is independent of data type. Using function templates we can reduces the size of the code and makes the maintenance code easy. Syntax:
Specialized template object General template object General template object How does template specialization work? When we write any template based function or class, compiler creates a copy of that function/class whenever compiler sees that being used for a new data type or new set of data types(in case of multiple template arguments).
When instantiating a function template for a given type, the compiler stencils out a copy of the templated function and replaces the template type parameters with the actual types used in the variable declaration. This means a particular function will have the same implementation details for each instanced type (just using different types).
This means a particular function will have the same implementation details for each instanced type (just using different types). While most of the time, this is exactly what you want, occasionally there are cases where it is useful to implement a templated function slightly different for a specific data type.
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).
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