Search code examples
c++gccvisual-c++c++14generic-lambda

Returning a generic lambda expression from a function causes compiler warnings if an explicit return type is given


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.


Solution

  • 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.