Given the following code :
#include <memory>
#include <iostream>
using namespace std;
template<typename T>
void test(T & value) {
cout << "most generic" << endl;
}
template<typename T>
void test(shared_ptr<T> & value) {
cout << "shared_ptr" << endl;
}
class A {};
int main(int argc, char ** argv) {
A a;
shared_ptr<A> p(new A());
test(a);
test(p);
return 0;
}
Why is the call
test(p)
instantiating the second form of test with T = A instead of complaining that it cannot distinguish between the two signatures ?
Because even though they are both viable choices for overload resolution, the second function template is more specialized than the first one.
Per Paragraph 13.3.3/1 of the C++11 Standard:
[...] Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
— for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,
— the context is an initialization by user-defined conversion (see 8.5, 13.3.1.5, and 13.3.1.6) and the standard conversion sequence from the return type of F1 to the destination type (i.e., the type of the entity being initialized) is a better conversion sequence than the standard conversion sequence from the return type of F2 to the destination type. [...] or, if not that,
— F1 is a non-template function and F2 is a function template specialization, or, if not that,
— F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.6.2.
§ 14.5.6.2 then says how a function template is determined to be more specialized than another function template. In particular, per 14.5.6.2/2:
Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.
Formal definitions in the Standard can be pretty hard to decipher, but their complexity is normally meant to unambiguously make the language behave like we would naturally expect in most situations.
The intuitive expectations we could have about the overloads you provide is that the one accepting a std::shared_ptr<T>
should be selected when the argument is of type std::shared_ptr<int>
, because it appears to be dealing with std::shared_ptr<>
objects specifically and, therefore, it looks like a better (more specialized) candidate than the unconstrained overload.
The formal procedure for translating this intuitive expectation into an unambiguous set of rules may sound complex, but following it is not particularly hard in our situation, where we want to determine if this overload:
template<typename T>
void f(std::shared_ptr<T>);
Is more specialized than this:
template<typename U>
void f(U);
Although in this case it is easy for us to tell which one is more specialized just based on our intuition, a compiler must rely on an algorithm, and this algorithm must work in all situations.
In this case, the mechanism would go as follows:
T
for a type argument (any type argument), for instance int
, and instantiate the corresponding signature - the function parameter would have type std::shared_ptr<int>
;shared_ptr<int>
in this case) as its input, and deduce the type U
from it?U
will just be deduced to be std::shared_ptr<int>
;U
for any type argument, for instance bool
, and instantiate the corresponding signature - the function parameter would have type bool
;bool
) as its argument and deduce the type T
from it?T
so that std::shared_ptr<T>
would match bool
;Of course, things get slightly more complicated when there is more than one template parameter and more than one function parameter, but the mechanism is pretty much the same.
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