Search code examples
c++language-lawyerlifetimecoveritytemporary-objects

Temporary bound references to struct members


I was trying Coverity out on some code base and I got a warning on a code akin to

struct Foo
{
    std::string name;
};

Foo getFoo();
//...
const auto& name = getFoo().name;
useName(name);

Is this code valid?

I had the intuition that it's indeed invalid. But then, when searching for proofs, I've come across [class.temporary]/6.4, which seems to say that it's actually well-formed. Yet, Coverity issues a warning, and Coverity is surely written by some smart folks that can interpret the standard better than me.

The specific Coverity warning that's issued is

Dereferencing the returned or out-of-scope stack pointer will access an invalid location on the stack after its scope or after the function returns.

In whateverSurroundingFunction(): Pointer to a local stack variable returned or used outside scope (CWE-562)

on the subsequent usage of name. The preceding warning is that

out_of_scope: Temporary variable of type Foo goes out of scope

And a sort of MRE is this (kudos to @user4581301 in the comments).


SO has a bunch of questions about temporary bound references, but those that I've seen each has a slightly different context, hence here comes yet another question on them.


Solution

  • This code is well-formed. As the linked standard says, the lifetime of the temporary returned by getFoo() will be extended to the lifetime of the reference variable name.

    (emphasis mine)

    Whenever a reference is bound to a temporary or to a subobject thereof, the lifetime of the temporary is extended to match the lifetime of the reference

    The reference is bound to the subobject name of the temporary Foo directly, then the lifetime gets extended. Other ways, e.g. binding the reference via the member function returning reference to data member won't work.

    struct Foo
    {
        std::string name;
        std::string& get_name() { return name; }
    };
    
    Foo getFoo();
    

    then

    const auto& name = getFoo().get_name();
    std::cout << name << std::endl; // UB; name is dangled
    

    LIVE