Search code examples
c++c++11referencetemporary-objects

Why is the lifetime of an object "that is the complete object of a subobject" extended when the subobject is returned?


I am studying the extension of the lifetime of a temporary when bound to a const &, and I would like to understand the following case:

#include <string>
#include <iostream>

char const * foo()
{
    std::string s("tmp");
    return s.c_str();
}

int main()
{
    char const * const & p = foo();

    // Why is the lifetime of the std::string extended in the following line,
    // rather than just the lifetime of the char* itself?
    std::cout << p; // This prints 'tmp' in VS2013
}

As noted, in Visual Studio 2013, the code builds without error and the console prints tmp.

This seems odd to me because the object whose lifetime is being extended is a subobject of an object local to the function being called that is destroyed when that function is exited. There is no std::string on the stack as the return value whose lifetime can be extended by the compiler when it compiles the main function - there's just a char * return value, whose lifetime can be extended but which would be a dangling pointer.

But clearly, the lifetime is being extended!

The closest question I have found to this is here: Does "T const&t = C().a;" lengthen the lifetime of "a"? ... However, in this question, the code B const& b = A().b; references the complete object A on the stack inside the calling function, so that object is available to have its lifetime extended.

As noted, it seems to me that the object whose lifetime should be extended in my code sample is a char *, not the string to which the char * points. (That is to say, I would think the return value, being just the size of a char *, would itself have its lifetime extended, but that it would become a dangling pointer.)

I do not understand how it is possible for the lifetime of the std::string to be extended in the sample code above. Can someone explain why this satisfies the criteria of having the std::string's lifetime extended by the char const * const &, rather than just having the char *'s lifetime extended?


Solution

  • How about following the constructor and destructor of your object

    #include <string>
    #include <iostream>
    
    class string_like: public std::string {
    
    public:
      string_like(const char* str): std::string (str) {
        std::cout << "string_like() : \n";
    }
      ~string_like() {
        std::cout << "~string_like(): \n";
    }
    };
    
    char const * foo()
    {
      std::cout << "in foo(){} : \n" ;
    
      string_like  s("tmp");
    
      std::cout << "leaving foo(){}" << "\n";
      return s.c_str();
    }
    
    
    int main()
    {
      std::cout << "begin main()\n";
      std::cout << "calling foo() :" << "\n";
      char const * const & p = foo();
      std::cout << "after calling foo() :\n";
      std::cout << "still in main\n" ;
      std::cout << p << "\n"; // print using g++
      std::cout << "leave main()\n";
    
    }
    

    I got the following output by g++ :

    begin main()
    calling foo() :
    in foo(){} : 
    string_like() :
    leaving foo(){}
    ~string_like():  object is destroyed here
    after calling foo() :
    still in main
    tmp    
    leave main()