c++move-semanticsstdstring

Is data returned by std::string::c_str() still valid after the string is moved?


Let's say I have a function that sends const char * data asynchronously, and invokes a callback when data is sent or an error happens (I omitted error handling as irrelevant to the question):

void sendData( const char *data, std::function<void()> );

So, until the callback is invoked, I have to maintain the data passed to the function as valid.

One solution is to pass the result of std::string::c_str() and move this string into the callback:

 void send( std::string str )
 {
     auto data = str.c_str();
     sendData( data, [st=std::move(str)]() {} );
 }

So, str should be moved into the lambda, and when it is invoked, it should destroy the string at the end.

Of course, I can move the original string to a std::unique_ptr<std::string> and then move that pointer into the lambda, but this code is simpler.

So, is it safe to do so?


Solution

  • It is important to understand that the moved-from and moved-to objects are two distinct objects. They are not one object that, due to some mysterious process, disappears in one place and reappears somewhere else. That's not what a move is.

    A moved-from object still exists, and when it goes out of scope and gets destroyed its destructor gets invoked normally.

    The C++ standard itself does not really specify what exactly a move means. In your own classes you can define a move constructor and a move assignment operator to do anything. Including nothing. All that C++, the language itself, does for you is formally define the conditions that require a move constructor or a move assignment operator to be invoked. That's it. That's the C++ compiler's job: invoke the constructor or the assignment operator when they are qualified to do so. The scant explanation of what a move actually is, once they are invoked, is defined in the C++ standard as an assertion that: for classes defined in a C++ library a move leaves the moved-from object in some "valid but unspecified state".

    The pointer returned from c_str() is owned by its std::string. It is valid only until its owning object is modified in any way. Turning this object into some "valid but unspecified state" would do the trick. The pointer from c_str() is no longer valid. The fact that the std::string was moved somewhere else is immaterial and does not change that. There's nothing in the C++ standard's specification of c_str()'s semantics that further qualifies its requirements with respect to moved objects.