Consider the following example
void foo(const std::function<int()>& f) {
std::cout << f() << std::endl;
}
void foo(const std::function<int(int x)>& f) {
std::cout << f(5) << std::endl;
}
int main() {
foo([](){return 3;});
foo([](int x){return x;});
}
This does not compile, because the call to foo
is said to be ambiguous. As far as I understand this is due to the fact, that the lambda function is not a priori a std::function
but has to be casted to it and that there is a std::function
constructor that takes an arbitrary argument.
Maybe someone can explain to me why anyone would create an implicit constructor that takes an arbitrary argument. However my acutual question is whether there is a workaround, which allows to use the function signature of lambda functions to overload a the function foo
. I have tried function pointers, but that didn't work because capturing lambda functions can't be cast to a normal function pointer.
Any help is most welcome.
Function Signature A function's signature includes the function's name and the number, order and type of its formal parameters. Two overloaded functions must not have the same signature. The return value is not part of a function's signature.
No, you can not overload the lambda! Which is not possible, as the same variable name can not be reused in C++.
Lambdas can both capture variables and accept input parameters. A parameter list (lambda declarator in the Standard syntax) is optional and in most aspects resembles the parameter list for a function. auto y = [] (int first, int second) { return first + second; };
Creating a Lambda Expression in C++auto greet = []() { // lambda function body }; Here, [] is called the lambda introducer which denotes the start of the lambda expression. () is called the parameter list which is similar to the () operator of a normal function.
Your compiler is correct according to C++11. In C++14, a rule is added that says that the constructor template shall not participate in overload resolution unless the type of the argument is actually callable with the std::function
's argument types. Therefore, this code is supposed to compile in C++14, but not in C++11. Consider this to be an oversight in C++11.
For now, you can work around this by explicit conversion:
foo(std::function<int()>([](){return 3;}));
http://coliru.stacked-crooked.com/a/26bd4c7e9b88bbd0
An alternative to using std::function is to use templates. Templates avoid the memory allocation overhead associated with std::function. The template type deduction machinery will deduce the correct type of the lambda passed so the call site cast goes away. However you still have is disambiguate the overloads for the no-args vs args case.
You can do this using a trick with trailing return types that behave similar to enable_if.
template<typename Callable>
auto baz(Callable c)
-> decltype(c(5), void())
{
std::cout << c(5) << std::endl;
}
The above overload of baz will only be a valid overload candidate when the template parameter Callable can be called with an argument of 5.
You can put more advanced mechanisms on top of this to make it more general (ie. variadic pack expansion of args into callable) but I wanted to show the basic mechanism working.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With