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.