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_tto bothstd::shared_ptr<int>andstd::function<std::shared_ptr<int>()>.This means that calling
make_something<ptr_int>(nullptr)needs to do the same amount of conversions to transform thestd::nullptr_targument 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 thanType(cv- and ref- qualifications are discarded for this check). This means thatstd::function<Type()>overload is chosen.If you were to add a third more specialized overload, that would be chosen:
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.