Why does returning const reference from a lambda result in a temporary?

  • A+
Category:Languages

I have a situation where I have a member returning a const&, and then this result is being forwarded within a lambda, which has the same return type.

MSVC2017 identifies this situation as risky, and issues a warning: returning address of local variable or temporary. Empirical testing with clang and other compilers shows this is true across the board. What I do not understand, is why this is different than several method calls which all return the same type.

For example, this works perfectly:

class A { public:     const std::string& name() const { return m_name; } private:     std::string m_name; };  class B { public:     const std::string& name() const { return m_a.name(); } private:     A m_a; };  //... B b; std::cout << b.name(); 

Works as expected, no warnings/errors at compile or runtime.

But with a lambda, it doesn't:

class A { public:     const std::string& name() const { return m_name; } private:     std::string m_name; };  //... using Getter = std::function< const std::string&() >; A a; Getter g = [&a] { return a.name(); }; std::cout << g(); 

results in a crash, or at least printing corrupted memory

Can someone point me to some info about why this does not work? I would generally expect it to work the same...


The return type of your lambda is not a reference. This is the cause of all your problems.

Your lambda returns a copy of the name. You are storing this lambda in a std::function returning a const std::string&, which means that effectively, you will return a reference to that copy which will get destroyed as soon as std::function's call operator returns!1

Naturally, the fix is to change the return type of the lambda:

Getter g = [&a]() -> const std::string& { return a.name(); };  // or Getter g = [&a]() -> auto& { return a.name(); }; // or if you are feeling fancy :P Getter g = [&a]() -> decltype(auto) { return a.name(); }; 

1: To expand a bit on this, you can imagine std::function's implementation as something like this (only the relevant parts are shown and massively simplified):

template<typename R, typename... Ts> struct function<R(Ts...)> {   R operator()(Ts... Args) const {     return callInternalFunctionObject(Args...); // here: copy will get destructed   } }; // R=const std::string&, and Ts is empty 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: