I have a function whose return type is a simple generic lambda expression. (The lambda returned by this function is eventually passed as an argument to STL algorithms like std::transform() or std::accumulate().)

When the lambda does not have an explicit return type, the compiler emits no warnings:

inline auto AccumulateInto() {
    return [](const auto& src, const auto& dst) {return dst + src; };
}

When the lambda has an explicit return type specified:

inline auto AccumulateInto() {
    return [](const auto& src, const auto& dst) -> decltype(dst) {return dst + src; };
}

both compilers emit these similar warnings:

GCC: returning reference to temporary [-Wreturn-local-addr]

MSVC: returning address of a local variable or temporary

Should this warning be heeded because it indicates a shortcoming in the approach (possible undefined behavior!) and should be refactored? or are they "noise"?

I can't determine why specifying the return type explicitly would cause the returned expression to be a "temporary" when it wouldn't be otherwise. Please explain, thanks!

EDIT (to add more context):

The desire to use auto arguments to the lambda is that src and dst might be different types (e.g. one is std::uint8_t and one is std::uint_16t), but I want the return to ALWAYS be the same type as the dst argument, regardless of the type of src.

1

There are 1 answers

10
max66 On BEST ANSWER

The point is that decltype(dst) is auto const & (where you can see auto as a template type`, so the lambda return a reference (I repeat: a reference) to a constant object.

The problem is that is a reference to

dst + src

that is: a reference to a temporary value, the temporary object created from the operation dst + src, that doesn't exist anymore when the lambda conclude the execution.

A possible solution: remove -> decltype(dst) or change it to -> decltype(dst+src), so you return a value, not a reference.

Another way (require more typewriting in this case but can be prefereble in more complex cases) could be remove the reference part from the type returned from decltype().

So

-> std::remove_reference_t<decltype(dst)>

or, as suggested by Jarod42, also

-> std::decay_t<decltype(dst)>

If the type of dst support the unary operator + (returning the same type of dst), another simple solution can be

-> decltype(+dst)

This way, +dst is an expression, not a variable, so decltype(+dst) isn't a reference anymore.