c++language-lawyerlifetimetemporary-objects

Is this access to temporary safe according to the C++ standard?


I recently found the following code pattern in a C++ code base, and I am now wondering if it is safe according to the C++ standard. (The real code passed the const char* pointer through several function layers, but I condensed it down to its essential form.)

#include <string>

class NetworkSocket
{
public:
    void SetPort(const char* port)
    {
        m_port = port;
    }

private:
    std::string m_port;
};

int main()
{
    const int     port   = 42;
    NetworkSocket socket = {};

    socket.SetPort(std::to_string(port).c_str());
}

Specifically, I want to know if the call to SetPort() is safe, or if we are invoking undefined behavior, or accessing (potentially) deleted memory.

I tried to figure it out myself using cppreference, but got stuck in the definitions. I got as far as:

  1. std::to_string() returns a prvalue, thus creating a temporary object.
  2. The call to c_str() extends the temporary's lifetime. (At least since C++17, before I am not sure.)
  3. From here on I am not sure. It seems that the lifetime of the temporary ends here, making the access to the const char* pointer undefined behavior. However, when I tried running the code through clang`s undefined behavior sanitizer no problems were found... https://godbolt.org/z/EfcvePoWe

Please quote the relevant standard rules, and explain the differences between the standard versions (if there are any).


Solution

  • This is well-defined.

    12.2 Temporary objects [class.temporary]

    ... Temporary objects are destroyed as the last step in evaluating the full- expression ([intro.execution]) that (lexically) contains the point where they were created.

    1.9 Program execution [intro.execution]

    ... A full-expression is an expression that is not a subexpression of another expression.

    Here:

    socket.SetPort(std::to_string(port).c_str());
    

    The temporary object is the std::string object that's the return value from std::to_string. The full-expression is, unremarkably, this entire expression. So the temporary object -- the one that owns its c_str() -- gets destroyed after the call to SetPort() returns. The End.

    (there are two exceptions to this rule, they do not apply here).