In the example below, why does the last one calls the function overload with std::function as parameter?
#include <iostream>
#include <functional>
#include <memory>
template <class Type>
void make_something(Type&& a){
std::cout<<"Type&& overload"<<std::endl;
}
template <class Type>
void make_something(std::function<Type()>){
std::cout<<"std::function overload"<<std::endl;
}
int main(){
make_something<int>(1); // prints "Type&& overload"
make_something<int>(nullptr); // prints "std::function overload"
make_something<int*>(nullptr); // prints "Type&& overload"
using ptr_int = std::shared_ptr<int>;
make_something<ptr_int>(nullptr); // prints "std::function overload" ... why?
}
There is an implicit conversion from std::nullptr_t
to both std::shared_ptr<int>
and std::function<std::shared_ptr<int>()>
.
This means that calling make_something<ptr_int>(nullptr)
needs to do the same amount of conversions to transform the std::nullptr_t
argument into the function argument (a user-defined conversion sequence).
If these were both non-template functions, this would be ambiguous. Since they are templates, the tiebreakers for templates can be used.
std::function<Type()>
is more specialised than Type
(cv- and ref- qualifications are discarded for this check). This means that std::function<Type()>
overload is chosen.
If you were to add a third more specialized overload, that would be chosen:
template <class Type>
void make_something(std::function<Type*()>){
std::cout<<"std::function Type* overload"<<std::endl;
}
This is generally used when the type is deduced (e.g., if you called make_something(std::function<int()>{})
, it would be ambiguous without the template rules), but has this unexpected behaviour when you specify the template argument.