Consider this simple example code:
#include <functional>
#include <iostream>
void f(bool _switch) {
std::cout << "Nothing really" << std::endl;
}
void f(std::function<double (int)> _f) {
std::cout << "Nothing really, too" << std::endl;
}
int main ( int argc, char* argv[] ) {
f([](int _idx){ return 7.9;});
return 0;
}
It fails to compile:
$ g++ --std=c++11 main.cpp
main.cpp: In function ‘int main(int, char**)’:
main.cpp:15:33: error: call of overloaded ‘f(main(int, char**)::<lambda(int)>)’ is ambiguous
main.cpp:15:33: note: candidates are:
main.cpp:6:6: note: void f(bool)
main.cpp:10:6: note: void f(std::function<double(int)>)
However if I replace the second function with a reference argument it compiles fine. And again if it is replaced by a const reference it fails.
So I have some questions concerning this example:
- Why is a lambda function implicitly convertible to
bool
in the first place? - Why does taking a std::function reference solve the ambiguity?
- And most important to me, how can I avoid this problem? I need the second function to take either a (copy of a) std::function or a const reference to it.
this gives us a pseudo-concept
invokable
.We can then use it like:
and bob is your uncle.
The real problem is that
std::function<double(int)>
's constructor does not do a similar test, and instead claims (falsely) that it can be constructed from anything at all. This is a flaw in the standard, one I suspect will be fixed once concepts are standardized.You can get rid of the implicit conversion by creating a helper class
Should you ever want to call
f
with eg. a pointer and expect it to call the first overload you will have to cast it to eitherbool
or add a corresponding constructor / cast to the helper class though.A lambda function with no capture can be converted to a regular function pointer, which then has a standard conversion to a bool.
If you take the
std::function
by non-const reference, then that eliminates it as a candidate, since converting the lambda to astd::function
requires a temporary, and a temporary cannot bind to a non-const reference. That just leavesf(bool)
as a candidate, so there is no ambiguity.There are many ways you could avoid the ambiguity. For example, you could create a
std::function
variable first:or you could cast the lambda:
You could have a helper function:
or you could grab the particular function you are interested in:
One more option to Vaughn Cato's answer:
Now the second overload is a template, so it is chosen for a lambda (or anything), and the first is chosen for
bool
. So callingf
is no more complex than needed.But, one problem with that is if you want to add more overloads, and another is that the first overload will only be called if there is an exact match to
bool
.