Consider the following code:
#include <iostream>
#include <functional>
using namespace std;
int main() {
auto f = [](int a = 3) {cout << a << endl; };
f(2); // OK
f(); // OK
auto g = f;
g(2); // OK
g(); // OK
function<void(int)> h = f;
h(2); // OK
h(); // Error! How to make this work?
return 0;
}
How can I declare h
to behave the same as f
and g
?
Creating a Lambda Function The lambda operator cannot have any statements and it returns a function object that we can assign to any variable.
Defaults in Python Lambda ExpressionIn Python, and in other languages like C++, we can specify default arguments.
A Python lambda function behaves like a normal function in regard to arguments. Therefore, a lambda parameter can be initialized with a default value: the parameter n takes the outer n as a default value. The Python lambda function could have been written as lambda x=n: print(x) and have the same result.
A lambda function can have any number of parameters, but the function body can only contain one expression.
std::function
has one fixed signature. This was a design choice, not a hard requirement. Writing a pseudo std::function
that supports multiple signatures isn't hard:
template<class...Sigs>
struct functions;
template<>
struct functions<> {
functions()=default;
functions(functions const&)=default;
functions(functions&&)=default;
functions& operator=(functions const&)=default;
functions& operator=(functions&&)=default;
private:
struct never_t {private:never_t(){};};
public:
void operator()(never_t)const =delete;
template<class F,
std::enable_if_t<!std::is_same<std::decay_t<F>, functions>{}, int>* =nullptr
>
functions(F&&) {}
};
template<class S0, class...Sigs>
struct functions<S0, Sigs...>:
std::function<S0>,
functions<Sigs...>
{
functions()=default;
functions(functions const&)=default;
functions(functions&&)=default;
functions& operator=(functions const&)=default;
functions& operator=(functions&&)=default;
using std::function<S0>::operator();
using functions<Sigs...>::operator();
template<class F,
std::enable_if_t<!std::is_same<std::decay_t<F>, functions>{}, int>* =nullptr
>
functions(F&& f):
std::function<S0>(f),
functions<Sigs...>(std::forward<F>(f))
{}
};
use:
auto f = [](int a = 3) {std::cout << a << std::endl; };
functions<void(int), void()> fs = f;
fs();
fs(3);
Live example.
This will create a separate copy of your lambda per overload. It is even possible to have different lambdas for different overloads with careful casting.
You can write one that doesn't do this, but it would basically require reimplementing std::function
with a fancier internal state.
A more advanced version of the above would avoid using linear inheritance, as that results in O(n^2) code and O(n) recursive template depth on the number of signatures. A balanced binary tree inheritance drops that down to O(n lg n) code generated and O(lg n) depth.
The industrial strength version would store the passed in lambda once, use small object optimization, have a manual pseudo-vtable that used a binary inheritance strategy to dispatch the function call, and store dispatched-to function pointers in said pseudo-vtable. It would take O(# signatures)*sizeof(function pointer) space in a per-class (not per-instance) basis, and use about as much per-instance overhead as std::function
would.
But that is a bit much for a SO post, no?
start of it
I like the nice solution proposed by @Yakk.
That said, you can do something similar by avoiding the std::function
s and using a proxy function for the lambda, variadic templates and std::forward
, as it follows:
#include<functional>
#include<utility>
#include<iostream>
template<typename... Args>
void f(Args&&... args) {
auto g = [](int a = 3) { std::cout << a << std::endl; };
g(std::forward<Args>(args)...);
};
int main() {
f(2);
f();
}
Put the above ideas in a functor and you'll have a function-like object that does almost the same.
It has the problem that parameters are not strictly verified.
Anyway, it will fail at compile time if misused.
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