So there are three main things I use lambdas for (NOTE: The Reddit numbered list functionality is literally so shitty that I cannot put any code blocks between numbered entries whatsoever, so you get no numbers): They make it easier to pass a function as an argument. Consider the situation where you have a very simple thing to do, but creating an entire separate function is both harder to maintain as well as more difficult to mentally parse. For example: int example(std::vector nums) { return std::accumulate(nums.begin(), nums.end(), 0, [](int acc, int num){ return num > 0 ? acc + num : acc; }); } They allow you to easily bind arguments for functions without needing to use the unnecessarily complicated std::bind. Before lambdas, C++ tried to get first class function using std::function and std::bind. The std::function thing mostly remains, but std::bind has been wholesale replaced by lambdas. struct MyObject { int doSomething(int argument); }; int deriveInfo(std::function f); int example() { MyObject o; // Instead of doing this: // return deriveInfo(std::bind(&MyObject::doSomething, &o)); // You do this: return deriveInfo(&o{ return o.doSomething(arg); }); } They allow you to create function objects without needing to do all of the class or struct definitions manually. A lambda is effectively an object that runs the function provided upon a call to operator(). As such, if you need to have multiple different function objects passed to the same object or template, this makes it very easy to do so. constexpr static auto A = [](int a, int b){ return example(a * b); }; constexpr static auto B = [](int a, int b){ return example(a + b); }; constexpr static auto C = [](int a, int b){ return example(a - b); }; template constexpr static inline void doSomething(Func f) { f(10, 5); } void someFunc() { doSomething(A); doSomething(B); doSomething(C); } I find lambdas to be quite useful and would be very disappointed if I was forced to make it work with just function pointers. Answer from WorldWorstProgrammer on reddit.com
🌐
Shaharmike
shaharmike.com › cpp › lambdas-and-functions
Under the hood of lambdas and std::function | Shahar Mike's Web Spot
February 23, 2016 - std::function is a templated object that is used to store and call any callable type, such as functions, objects, lambdas and the result of std::bind.
🌐
Reddit
reddit.com › r/cpp_questions › when to use a lambda over a function pointer?
r/cpp_questions on Reddit: When to use a lambda over a function pointer?
September 10, 2023 -

Question seems simple enough.
I've never really understood then point of lambdas. To me the code seems messy, why not use a function pointer instead?

Top answer
1 of 10
25
So there are three main things I use lambdas for (NOTE: The Reddit numbered list functionality is literally so shitty that I cannot put any code blocks between numbered entries whatsoever, so you get no numbers): They make it easier to pass a function as an argument. Consider the situation where you have a very simple thing to do, but creating an entire separate function is both harder to maintain as well as more difficult to mentally parse. For example: int example(std::vector nums) { return std::accumulate(nums.begin(), nums.end(), 0, [](int acc, int num){ return num > 0 ? acc + num : acc; }); } They allow you to easily bind arguments for functions without needing to use the unnecessarily complicated std::bind. Before lambdas, C++ tried to get first class function using std::function and std::bind. The std::function thing mostly remains, but std::bind has been wholesale replaced by lambdas. struct MyObject { int doSomething(int argument); }; int deriveInfo(std::function f); int example() { MyObject o; // Instead of doing this: // return deriveInfo(std::bind(&MyObject::doSomething, &o)); // You do this: return deriveInfo([&o](int arg){ return o.doSomething(arg); }); } They allow you to create function objects without needing to do all of the class or struct definitions manually. A lambda is effectively an object that runs the function provided upon a call to operator(). As such, if you need to have multiple different function objects passed to the same object or template, this makes it very easy to do so. constexpr static auto A = [](int a, int b){ return example(a * b); }; constexpr static auto B = [](int a, int b){ return example(a + b); }; constexpr static auto C = [](int a, int b){ return example(a - b); }; template constexpr static inline void doSomething(Func f) { f(10, 5); } void someFunc() { doSomething(A); doSomething(B); doSomething(C); } I find lambdas to be quite useful and would be very disappointed if I was forced to make it work with just function pointers.
2 of 10
13
Keep in mind a lambda is syntactical sugar for a functor, not a function pointer. Function pointers are very bad from a performance perspective since it prevents inlining. Lambdas and functors are equivalent though.
Top answer
1 of 2
80

It's because a lambda function is not a std::function<...>. The type of

auto lambda = [](const std::string& s) { return std::stoi(s); };

is not std::function<int(const std::string&)>, but something unspecified which can be assigned to a std::function. Now, when you call your method, the compiler complains that the types don't match, as conversion would mean to create a temporary which cannot bind to a non-const reference.

This is also not specific to lambda functions as the error happens when you pass a normal function. This won't work either:

int f(std::string const&) {return 0;}

int main()
{
    std::vector<int> vec;
    C<int> c;
    c.func(vec, f);
}

You can either assign the lambda to a std::function

std::function<int(const std::string&)> lambda = [](const std::string& s) { return std::stoi(s); };

,change your member-function to take the function by value or const-reference or make the function parameter a template type. This will be slightly more efficient in case you pass a lambda or normal function pointer, but I personally like the expressive std::function type in the signature.

template<typename T>
class C{
    public:
    void func(std::vector<T>& vec, std::function<T( const std::string)> f){
        //Do Something
    }

    // or
    void func(std::vector<T>& vec, std::function<T( const std::string)> const& f){
        //Do Something
    }

    // or
    template<typename F> func(std::vector<T>& vec, F f){
        //Do Something
    }
};
2 of 2
36

It's because the argument (std::function) is a reference. It should be:

void func(std::vector<T>& vec, std::function<T(const std::string&)> f)
                                                                ^  ^
                                                                   |
                                                                   f not a reference

So that the argument can be converted to the parameter type.

Also, the type of the function should match. I.e. it should accept a string reference.

🌐
GitHub
gist.github.com › cd626ea3685fd5e8bf14
Convert lambda to std::function · GitHub
Convert lambda to std::function. GitHub Gist: instantly share code, notes, and snippets.
🌐
Cppreference
en.cppreference.com › w › cpp › utility › functional › function.html
std::function - cppreference.com
#include <functional> #include <iostream> struct Foo { Foo(int num) : num_(num) {} void print_add(int i) const { std::cout << num_ + i << '\n'; } int num_; }; void print_num(int i) { std::cout << i << '\n'; } struct PrintNum { void operator()(int i) const { std::cout << i << '\n'; } }; int main() { // store a free function std::function<void(int)> f_display = print_num; f_display(-9); // store a lambda std::function<void()> f_display_42 = []() { print_num(42); }; f_display_42(); // store the result of a call to std::bind std::function<void()> f_display_31337 = std::bind(print_num, 31337); f_di
🌐
W3Schools
w3schools.com › cpp › cpp_functions_lambda.asp
C++ Lambda Functions
You can pass values into a lambda just like a regular function: #include <iostream> using namespace std; int main() { auto add = [](int a, int b) { return a + b; }; cout << add(3, 4); return 0; } Result: 7 Try it Yourself » · You can also pass a lambda function as an argument to another function.
🌐
Cppreference
en.cppreference.com › w › cpp › language › lambda.html
Lambda expressions (since C++11) - cppreference.com
The lambda expression is a prvalue expression of unique unnamed non-union non-aggregate class type, known as closure type, which is declared (for the purposes of ADL) in the smallest block scope, class scope, or namespace scope that contains the lambda expression.
Find elsewhere
🌐
Lesleylai
lesleylai.info › en › std-function
What is std::function in C++, and why do we need them? | Lesley Lai
January 18, 2021 - To make func accepts both lambda and lambda2, std::function needs to have constructors that take any function object or plain function that satisfies its signature.
🌐
Rip Tutorial
riptutorial.com › std::function with lambda and std::bind
C++ Tutorial => std::function with lambda and std::bind
#include <iostream> #include <functional> using std::placeholders::_1; // to be used in std::bind example int stdf_foobar (int x, std::function<int(int)> moo) { return x + moo(x); // std::function moo called } int foo (int x) { return 2+x; } int foo_2 (int x, int y) { return 9*x + y; } int main() { int a = 2; /* Function pointers */ std::cout << stdf_foobar(a, &foo) << std::endl; // 6 ( 2 + (2+2) ) // can also be: stdf_foobar(2, foo) /* Lambda expressions */ /* An unnamed closure from a lambda expression can be * stored in a std::function object: */ int capture_value = 3; std::cout << stdf_foobar(a, [capture_value](int param) -> int { return 7 + capture_value * param; }) << std::endl; // result: 15 == value + (7 * capture_value * value) == 2 + (7 + 3 * 2) /* std::bind expressions */ /* The result of a std::bind expression can be passed.
🌐
Cplusplus
cplusplus.com › forum › general › 223816
lambda to std::function template - C++ Forum
October 27, 2017 - Do you realize that a lambda is a callable target? http://en.cppreference.com/w/cpp/utility/functional/function There is an example where a lambda is stored in a std::function, thereby wrapping the lambda. Is that what you are after?
🌐
Medium
medium.com › @briankworld › introduction-to-c-lambdas-and-using-them-with-standard-library-algorithms-bef29ef80dd8
Introduction to C++ Lambdas and Using Them with Standard Library Algorithms | by Brian | Medium
May 6, 2023 - It allows you to define a function on the fly and pass it as a parameter to another function or use it directly. ... Lambda expressions are often used in C++ with STL algorithms like std::for_each, std::transform, and std::sort to provide custom ...
🌐
Alandefreitas
alandefreitas.github.io › moderncpp › basic-syntax › functions › lambda
Lambda Functions - Modern C++
Lambdas in function calls · Store lambda as std::function · Capturing values in lambda function · Parameter type deduction · Binding parameters to the function · Bind a single parameter · Keep parameters and convert the return type · Sorting with lambdas ·
🌐
MC++ BLOG
modernescpp.com › index.php › c-core-guidelines-function-objects-and-lambas
C++ Core Guidelines: Function Objects and Lambdas – MC++ BLOG
September 29, 2017 - With C++14, we have generic lambdas; therefore, you can define a lambda expression such as [](auto a, auto b){ return a + b; };. What does that mean for the call operator of AddObj? I assume you can already guess it. The call operator becomes a template. I want to emphasize it explicitly: a generic lambda is a function template.
🌐
Johannesugb
johannesugb.github.io › cpu-programming › how-to-pass-lambda-functions-in-C++
How To Pass Lambda Functions in C++ (By Value, By L-Value Reference, By Universal Reference) - Johannes Unterguggenberger
January 9, 2021 - Figure 1: Two versions of a function, the left accepts a lambda by value, the right accepts it by const reference. Figure 2: The assembly code of the two versions from Figure 1 compared with each other. Where it can make a difference is when the lambda captures data. If a lambda captures large amounts of data, that data would have to be copied whenever a lambda is passed by value. Let us assume that a lambda captures a large vector by value, as shown in Code Listing 2. std::vector<uint8_t> blob; auto lambda = [blob](){ };
🌐
DEV Community
dev.to › pratikparvati › lambda-functions-in-c-39dh
Lambda Functions in C++ - DEV Community
June 13, 2021 - There are three possible ways to give lambda a name. we can use std::function<> to hold the function pointer.
🌐
a place to jot
wirepair.org › 2023 › 11 › 15 › stdfunction-lambda-and-testing
std::function, Lambda and Testing – a place to jot
November 15, 2023 - It inherits from SocketSender and implements the override’s necessary for Send/SendTo. But it also has a single public member std::function<size_t(const void *Buffer, const int Len)> SendFn = nullptr;. This std::function is what I can override in my tests.
🌐
Learn C++
learncpp.com › cpp-tutorial › introduction-to-lambdas-anonymous-functions
20.6 — Introduction to lambdas (anonymous functions) – Learn C++
January 3, 2020 - Although we don’t know the type of a lambda, there are several ways of storing a lambda for use post-definition. If the lambda has an empty capture clause (nothing between the hard brackets []), we can use a regular function pointer. std::function or type deduction via the auto keyword will also work (even if the lambda has a non-empty capture clause).
Top answer
1 of 1
7

Questions & answers

“Are there any drawbacks to my implementation compared to the traditional implementation of std::function using heap allocation and virtual calls? Perhaps statically allocating the lambda is not a good idea?”

Yes, definitely a bad idea, with numerous drawbacks.

Because there are no compile-time checks to ensure that you’re actually using a lambda, this type is very brittle and very dangerous. Today I may write code like this:

auto x = 0;
auto y = 0;

auto func_x = [&x] { ++(*x); };
auto func_y = [&y] { ++(*y); };

// ... later:
auto function_x = Function<void>{func_x};
auto function_y = Function<void>{func_y};

No problems there. (Well, I mean, yes, problems, but not for this specific presumed use case. But we’ll get back to that.)

But then, later in the development process, someone refactors those two lambdas into this:

struct indirect_incrementer
{
    int* p_val = nullptr;

    auto operator()() { ++(*p_val); }
};

// then the following two lines:
//auto func_x = [&x] { ++(*x); };
//auto func_y = [&y] { ++(*y); };
// become:
auto func_x = indirect_incrementer{&x};
auto func_y = indirect_incrementer{&y};

It’s an innocent and perfectly logical refactor… but now everything blows up, because your assumption that every function object type Function is constructed with no longer holds. The static variable function is initialized with a copy of func_x, and so calling function_y does not call func_y, it calls func_x.

This isn’t just a problem with using function objects. Even if you somehow completely banned the use of function objects with Function—like, say, with some kind of linter or something—and made 100% sure that Function is always called with a legit lambda object, the problem still exists. Because despite your thinking, it is possible for a lambda type to be non-unique. It’s even quite trivial to do… just call the function that contains the lambda more than once:

auto render()
{
    // ... [snip] ...

    // somewhere in your render function, you use Function:
    auto func = Function<int, int>{[&] (int i) { return ++i; }};

    // ... [snip] ...
}

// elsewhere, in your main game loop:
while (not done)
{
    input();
    update();
    render(); // <- !!!
}

render() is called in a loop, but only the first loop will initialize the static function variable. Meaning every other time you use func, it will be with dangling references to the locals in that first render() call.

Now you could “fix” that problem with various hacks, basically detecting whether function was previously initialized, or counting the number of times it was initialized, or something like that. But it won’t fix the other problems.

The other big problem with using a static variable to hold a copy of the lambda is that you’re quietly cheating C++’s scope rules. This could lead to frustrating and nasty surprises. For example:

auto p_weak = std::weak_ptr<int>{};

// in some more restricted scope:
{
    auto p = std::make_shared<int>();
    p_weak = p;

    auto func = Function<void>{[p] { if (p) ++(*p); }};

    // use func somehow

    // scope is ending, so func and p are being destroyed, right?
    //
    // ... *right*?
}

if (auto p = p_weak.lock(); p)
    std::cerr << "memory leak!";

Turns out, p is never actually destructed; the lambda takes a copy, which is then copied into the static function variable, which then holds on to it… forever (well, until after main() returns, at least). This is not just a shared_ptr problem; I just used shared_ptr to be able to access otherwise lost memory. Any value that gets captured by the lambda is never destructed during the lifetime of the program. In other words, the more you use Function—even when every use avoids the other problems mentioned—the more you leak memory.

That’s probably why you’re seeing such large increases in speed: the function object is only being copy-constructed once—even when that’s incorrect—and it’s never being destructed.

(Also, std::function has a ton of very important and useful other abilities and benefits Function doesn’t offer, like the ability to be null-constructed, and reseated. Mere type-erasure is useful, I suppose, but probably not useful enough on its own… especially when simple function pointers can do that (and more!).)

So yes, using a static variable to get around dynamic allocation is not a good idea.

Code review

template <typename Lambda>
Function(Lambda f) {
static auto function = f;
func = + [] (Args... args) -> R {
    return function(args...);
};
};

Proper indentation would definitely help make this more readable. It would also highlight the stray semicolon you have at the end.

The fact that you’re copying f into function makes the whole class unusable with move-only lambda types:

auto p = std::make_unique<int>();

auto lambda_p = [p = std::move(p)] { if (p) { *p = 42; } };

auto func_p = Function<void>{lambda_p}; // won't compile

// (also, as mentioned previously, p won't ever be freed)

You should also use forwarding references in all the function calls. Right now every function argument will be taken by-value, which will be at least slow, and probably won’t compile in a lot of cases.

Function(const Function& other) : func(other.func) {}

This is entirely unnecessary.

inline auto operator() (Args... args) {
    return func(args...);
}

The inline keyword does nothing here.

Again, you should really be using forwarding references.

The whole interface of Function is also rather clunky. It seems specious to have to give the return and argument types, when they’re obvious and deduce-able from the lambda itself. It would be much nicer to be able to write:

auto f1 = Function{[&] (int z) {return x + y + z}};
// f1 is deduced as Function<int, int>

This is a situation where deduction guides would be useful.

Summary

You can’t get away with avoiding having function object wrappers actually wrap the function objects. Put that way, it should seem pretty obvious.

Your type-erasing wrapper either needs to dynamically allocate the memory to copy a function object, or use the small object optimization, or both. Trying to hide the wrapped object in a static variable is cheating. You may get away with it for a little while… but you will eventually be caught, either via memory leaks, reading/writing dangling references, or simply reading/writing to the wrong objects.