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".
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…