Reputation: 2236
This code fails to compile, and gives the error:
test.cpp:12:4: error: no matching function for call to 'func'
func(std::max<int>);
^~~~
test.cpp:4:6: note: candidate function not viable: no overload of 'max' matching 'std::function<const int &(const int &, const int &)>' for 1st argument
void func(std::function<const int &(const int &, const int &)> a)
^
1 error generated.
#include<iostream>
#include<functional>
#include <algorithm>
void func(std::function<const int &(const int &, const int &)> a)
// void func(const int &a(const int &, const int &)) //this one works
{
std::cout << a(4,5) << std::endl;
}
int main(int argc, char const *argv[])
{
func(std::max<int>);
}
I previously used a function pointer with the same argument signature as std::function
and that worked. What's going on here?
Upvotes: 2
Views: 823
Reputation: 275385
A std function is not a function pointer.
The name of a template function experiences overload resolution when called or when cast to a function pointer. It does not experience overload resolution when passed to a std function.
std::max<int>
could refer to more than one function; ones taking an initializer list or two arguments that are int
s, and each of those have overloads that take comparators or not.
With overload resolution, all but one of these are discarded. Without, the result is ambiguous.
int x00 = std::max<int>( std::initializer_list<int>{1,2,3,4} )
int x01 = std::max<int>( 2, 4 );
int x10 = std::max<int>( std::initializer_list<int>{1,2,3,4}, [](int a, int b){ return b>a; } )
int x11 = std::max<int>( 2, 4, [](int a, int b){ return b>a; } );
by saying std::max<int>
you are saying one of those 4 are being used.
And yes, there is only one of them that work with std::function
, but the C++ language code that calls the std::function
doesn't know this, so it says the result is ambiguous. (note that two of the above are an infinite set of possible signatures).
Meanwhile,
int const&(*ptr)(int const&,int const&) = std::max<int>;
here, we are doing overload resolution. We pick the std::max<int>
that takes two int
s.
std::function<int const&(int const&, int const&)> f = std::max<int>;
here we don't do overload resolution on std::max
.
An easy fix for this is:
std::function<int const&(int const&, int const&)> f =
[](auto&&...args)->decltype(auto)
{ return std::max<int>(decltype(args)(args)...); };
which I sometimes write as a macro
#define RETURNS(...) \
noexcept(noexcept( __VA_ARGS__ )) \
-> decltype( __VA_ARGS__ ) \
{ return __VA_ARGS__; }
#define CALL(...) \
[](auto&&...args) \
RETURNS( __VA_ARGS__( decltype(args)(args)... ) )
giving us:
std::function<int const&(int const&, int const&)> f = CALL(std::max<int>);
here I delay overload resolution until after we invoke the callable object we pass f
. At that point the types are known, so everything works.
Upvotes: 6