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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With