I am trying to understand why std::function
is not able to distinguish between overloaded functions.
#include <functional>
void add(int,int){}
class A {};
void add (A, A){}
int main(){
std::function <void(int, int)> func = add;
}
In the code shown above, function<void(int, int)>
can match only one of these functions and yet it fails. Why is this so? I know I can work around this by using a lambda or a function pointer to the actual function and then storing the function pointer in function. But why does this fail? Isn't the context clear on which function I want to be chosen? Please help me understand why this fails as I am not able to understand why template matching fails in this case.
The compiler errors that I get on clang for this are as follows:
test.cpp:10:33: error: no viable conversion from '<overloaded function type>' to
'std::function<void (int, int)>'
std::function <void(int, int)> func = add;
^ ~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__functional_03:1266:31: note:
candidate constructor not viable: no overload of 'add' matching
'std::__1::nullptr_t' for 1st argument
_LIBCPP_INLINE_VISIBILITY function(nullptr_t) : __f_(0) {}
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__functional_03:1267:5: note:
candidate constructor not viable: no overload of 'add' matching 'const
std::__1::function<void (int, int)> &' for 1st argument
function(const function&);
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__functional_03:1269:7: note:
candidate template ignored: couldn't infer template argument '_Fp'
function(_Fp,
^
1 error generated.
EDIT - In addition to MSalters' answer, I did some searching on this forum and found the exact reason why this fails. I got the answer from Nawaz's reply in this post.
I have copy pasted from his answer here:
int test(const std::string&) {
return 0;
}
int test(const std::string*) {
return 0;
}
typedef int (*funtype)(const std::string&);
funtype fun = test; //no cast required now!
std::function<int(const std::string&)> func = fun; //no cast!
So why std::function<int(const std::string&)>
does not work the way funtype fun = test
works above?
Well the answer is, because std::function
can be initialized with any object, as its constructor is templatized which is independent of the template argument you passed to std::function
.
Another way to deal with this is with a generic lambda in C++14:
That will create a lambda function that will resolve things with no ambiguity. I did not forward arguments,
While it's obvious what you want, the problem is that
std::function
cannot influence overload resolution of&add
. If you were to initialize a raw function pointer (void (*func)(int,int) = &add
), it does work. That's because function pointer initialization is a context in which overload resolution is done. The target type is exactly known. Butstd::function
will take almost any argument that's callable. That flexibility in accepting arguments does mean that you can't do overload resolution on&add
. Multiple overloads ofadd
might be suitable.An explicit cast will work, i.e.
static_cast<void(*)(int, int)> (&add)
.This can be wrapped in a
template<typename F> std::function<F> make_function(F*)
which would allow you to writeauto func = make_function<int(int,int)> (&add)
It's obvious to us which function you intend to be chosen, but the compiler has to follow the rules of C++ not use clever leaps of logic (or even not so clever ones, as in simple cases like this!)
The relevant constructor of
std::function
is:which is a template that accepts any type.
The C++14 standard does constrain the template (since LWG DR 2132) so that it:
which means that the compiler will only allow the constructor to be called when
Functor
is compatible with the call signature of thestd::function
(which isvoid(int, int)
in your example). In theory that should mean thatvoid add(A, A)
is not a viable argument and so "obviously" you intended to usevoid add(int, int)
.However, the compiler can't test the "
f
is Callable for argument types ..." constraint until it knows the type off
, which means it needs to have already disambiguated betweenvoid add(int, int)
andvoid add(A, A)
before it can apply the constraint that would allow it to reject one of those functions!So there's a chicken and egg situation, which unfortunately means that you need to help the compiler out by specifying exactly which overload of
add
you want to use, and then the compiler can apply the constraint and (rather redundantly) decide that it is an acceptable argument for the constructor.It is conceivable that we could change C++ so that in cases like this all the overloaded functions are tested against the constraint (so we don't need to know which one to test before testing it) and if only one is viable then use that one, but that's not how C++ works.
As far as I can see, it's a Visual Studio problem.
c++11 standard (20.8.11)
but VisualStudio doesn't have that specialization
clang++ and g++ are perfectly fine with overloading std::functions
prior answers explain why VS doesn't work, but they didn't mention that it's VS' bug
Try:
Addresses to
void add(A, A)
andvoid add(int, int)
obvoiusly differes. When you point to the function by name it is pretty much imposible for compiler to know which function address do you need.void(int, int)
here is not a hint.