c++containersc++17temporary-objects

Lifetime extension of temporary container's element


Does prolonging the lifetime of a temporary object through const references also apply for elements of temporary containers, such as in the case of reference r in the program below?
Is that guaranteed by the C++ standard?
I've read that this sort of lifetime extension does apply for subobjects of complete objects, but do elements of standard containers qualify as subobjects of their respective container?

#include <unordered_map>
#include <string>
#include <iostream>

using my_map = std::unordered_map<std::string, int>;

my_map func() {
    return my_map{{"foo", 42}};
}

int main() {
    const auto& r = func()["foo"];
    std::cout << "r = " << r << std::endl; // Is r valid here?
    return 0;
}

Solution

  • No, r is not valid. r becomes an invalid reference to a temporary as soon as the statement is r = func()["foo"]; is over.

    Here's a proof of concept that extends upon your example code:

    #include <unordered_map>
    #include <string>
    #include <iostream>
    
    
    class Foo {
    public:
        int value;
    
        Foo() : value(0) {
            value = 0;
        }
        Foo(int n) : value(n) {
        }
        ~Foo() {
            std::cout << "~Foo(" << value << ")\n";
        }
    };
    
    using my_map = std::unordered_map<std::string, Foo>;
    
    my_map func() {
        return my_map{ {"foo", 42} };
    }
    
    int main() {
        const auto& r = func()["foo"];
        std::cout << "r.value = " << r.value << std::endl; // Is r valid here?
    }
    

    When I run the following program, I get these results:

    ~Foo(42)
    ~Foo(42)
    r.value = -572662307    # GARBAGE OUTPUT
    

    -572662307 is 0xdddddddd in hex, which is what Visual Studio's debug assigns to deleted memory that hasn't been reclaimed. So it's a strong indication that r is definitely invalid.

    This would be valid in your code and would print the expected value of 42:

    std::cout << "func['foo'] = " << func()["foo"] << std::endl;