I'm wondering if anyone knows why the following sample doesn't compile giving an ambiguous call to overload function error. If I replace the auto with a strongly typed functor signature, it then is able to properly distinguish between the two method overloads.
I noticed the same issue doesn't occur when not using std::function as my overload arguments. If my overloads take just a simple float and int, the compiler can properly distinguish between the two overloads even when using the auto keyword to define my input arguments. I'm compiling this in VisualStudio 2012. Could this just be an bug in the VS compiler? I don't have access to a machine with GCC or Clang on it right now, but does anyone know if this would compile there?
Compile Error: ambiguous call to overload function
class AmbiguousOverload
{
public:
static void OverloadedMethod(std::function<int()>) {}
static void OverloadedMethod(std::function<float()>) {}
};
int _tmain(int argc, _TCHAR* argv[])
{
auto func1 = []() -> float {
return 0.5f;
};
auto func2 = []() -> int {
return 12;
};
AmbiguousOverload::OverloadedMethod(func1);
AmbiguousOverload::OverloadedMethod(func2);
return 0;
}
Compiles
class AmbiguousOverload
{
public:
static void OverloadedMethod(std::function<int()>) {}
static void OverloadedMethod(std::function<float()>) {}
};
int _tmain(int argc, _TCHAR* argv[])
{
std::function<float()> func1 = []() -> float {
return 0.5f;
};
std::function<int()> func2 = []() -> int {
return 12;
};
AmbiguousOverload::OverloadedMethod(func1);
AmbiguousOverload::OverloadedMethod(func2);
return 0;
}
Also Compiles
class AmbiguousOverload
{
public:
static void OverloadedMethod(int) {}
static void OverloadedMethod(float) {}
};
int _tmain(int argc, _TCHAR* argv[])
{
auto v1 = 0.5f;
auto v2 = 12;
AmbiguousOverload::OverloadedMethod(v1);
AmbiguousOverload::OverloadedMethod(v2);
return 0;
}
std::function
had a greedy template
constructor that will attempt to construct it from anything at all you passed it1. std::function
is not very suitable for use in overload resolution. It worked in one case because perfect matches are preferred to conversion, but as noted it is fragile.
Note that lambdas and std::function
objects are unrelated types. std::function
knows how to wrap a lambda, but it can wrap any copyable invokable object. Lambdas are auto-created anonymously named classes that are copyable and invokable. std::function
is a class designed to type-erase invocation.
Imagine if your overrides took short
and long
. The auto x = 2.0
and short s = 2.0
would correspond to the auto x = lambda
and std::function<blah> f = lambda
cases. When you pick the type explicitly you cause a type conversion, and the types you picked explicitly had no ambiguity. But when you did auto
it took the real type -- and the real type was ambiguous.
SFINAE or tag dispatching using std::result_of
would let you handle these overrides manually.
In c++14 this changed a bit. Now the constructor only tries to swallow compatible arguments. But a function returning int
and one returning float
are both compatible with each other, so it won't help your specific case.
1 To a certain extent this is a flaw in std::function
. Its "universal" constructor should really only participate in overload resolution when the type passed in is both copyable and std::result_of_t< X( Args... ) >
can be converted to the result type of the std::function
. I suspect post-concepts this will be added to the standard, as post-concepts this is really easy to both write and express (and very little conforming code will be broken by it). However, in this particular case, this would not actually help, as int
and float
can be converted into each other, and there are no ways to say "this constructor will work, but really it isn't a preferred option".