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?
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.