Mochan Mochan - 4 months ago 35
C++ Question

std::functions and lambda function passing

I have a class that takes a std::function as a parameter which I assign a lambda function. It works in the constructor but it stops working after that. The debugger says f is "empty" after running the first line. Why?

#include <iostream>
#include <string>
#include <functional>

typedef std::function<void(std::string)> const& fn;

class TestClass
{
public:
TestClass(fn _f) : f(_f) { F(); }
void F() { f("hello"); };

private:
fn f;
};


int main()
{
TestClass t([](std::string str) {std::cout << str << std::endl; });

t.F();

return 0;
}


Calling t.F() causes a fault. Why?

I can solve it by changing it to the following:

int main()
{
fn __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);

t.F();

return 0;
}


but again, this does not work when I change fn to auto!

int main()
{
auto __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);

t.F();

return 0;
}


What is the explanation of why this is happening?

Answer

Note that (1) fn is defined as reference (to const); (2) lambda and std::function are not the same type; (3) You can't bind reference to object with different type directly.

For the 1st case,

TestClass t([](std::string str) {std::cout << str << std::endl; });
t.F();

A temporary lambda is created and then converted to std::function which is a temporary too. The temporary std::function is bound to the parameter _f of the constructor and bound to member f. The temporary will be destroyed after this statement, then f becomes dangled, when t.F(); it fails.

For the 2nd case,

fn __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();

A temporary lambda is created and then bound to reference (to const). Then its lifetime is extended to the lifetime of the reference __f, so the code is fine.

For the 3rd case,

auto __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();

lambda is created and then converted to std::function which is a temporary. The temporary std::function is bound to the parameter _f of the constructor and bound to member f. The temporary will be destroyed after this statement, then f becomes dangled, when t.F(); it fails.


(1) You could declare fn as non-reference like typedef std::function<void(std::string)> fn;, then std::function will be copied and every case would work well.
(2) Don't use names begin with double underscore, they're reserved in C++.