Consider the declaration of a class that contains a lambda function or a reference to it:
#include <utility>
template <class Func>
struct Holder
{
Holder(Func && func) : m_func(std::forward<Func>(func))
{
}
Func m_func;
};
template <class Func>
auto MakeHolder(Func && func)
{
return Holder<Func>(std::forward<Func>(func));
}
In the code below h1
is equivalent to h2
:
int main()
{
auto func = [](int val) { };
Holder h1 = MakeHolder(func);
Holder<decltype(func) &> h2(func);
return 0;
}
and the following code would not compile:
int main()
{
auto func = [](int val) { };
Holder<decltype(func)> h2(func);
return 0;
}
it is because func
is of type const Lambda &
but the constructor parameter is of type Lambda &&
.
Furthermore, the following code will crash:
auto MakeCorruptedHolder()
{
const std::shared_ptr<std::string> p = std::make_shared<std::string>("abc");
auto func = [p]()
{
std::cout << "Labda with " << *p << std::endl;
};
return MakeHolder(func);
}
int main()
{
Holder h = MakeCorruptedHolder();
h.m_func();
return 0;
}
But if we replace line 10 with
return MakeHolder(std::move(func));
it stops to crash, because now Func
is the value, but not the reference.
To prevent your brain from a collapse it is better to define Holder
class like this:
template <class Func>
struct Holder
{
Holder(Func func) : m_func(std::move(func))
{
}
std::decay_t<Func> m_func;
};
thus it will contain the value, whatever you do with it.