3

Is the following code guaranteed safe by the standard, with regards to std::string?

#include <string>
#include <cstdio>

int main() 
{
    std::string strCool = "My cool string!";
    const char *pszCool = strCool.c_str();
    strCool = pszCool;
    printf( "Result: %s", strCool.c_str() );
}

I've seen statements that indicate the result of c_str is only guaranteed safe to use until another method call on the same std::string is made, but it's not clear to me whether it's safe to pass that const char * back into the assignment method or not.

When tested using real-world compilers (GCC, Clang, and MSVC at their most recent versions) they all seem to support this behavior.

Furthermore, the compilers also support assigning a suffix of the string back to itself, e.g. strCool = pszCool + 3 in this example; the result will be that the string has the same value as what was passed into it.

Is this behavior guaranteed somehow, or am I just lucky that the standard libraries provided by the compilers I've tested support this case?

cigien
  • 57,834
  • 11
  • 73
  • 112
Daniel Jennings
  • 6,304
  • 3
  • 32
  • 42
  • Hi! You tagged this question `stl`, but it looks like you're using the C++ standard library. Which library are you using? See also [What's the difference between "STL" and "C++ Standard Library"?](https://stackoverflow.com/q/5205491/11082165) and the `stl` description. – Brian61354270 Oct 11 '21 at 23:49
  • 1
    It doesn't sound like you've "tested" anything. You merely saved a pointer to the C string, and verified nothing happened to crash. Look [here](https://stackoverflow.com/a/6456408/421195): `The c_str() result becomes invalid if the std::string is destroyed or if a non-const member function of the string is called. So, usually you will want to make a copy of it if you need to keep it around.` In other words, a) ensure the original std::string remains unchanged, and/or b) strcpy() to another buffer. – paulsm4 Oct 11 '21 at 23:56
  • 3
    Setting aside any morsels in the standard: I would expect a typical C++ library implementation to either allocate a new internal buffer before deleting the old one, or reuse the same buffer if the new string will fit. I would be surprised if the C++ standard mandates either behavior, so by default this becomes unspecified. And if the implementation chooses to reuse the buffer this becomes an overlapping copy, which is undefined behavior. That's my reasoning. – Sam Varshavchik Oct 11 '21 at 23:59
  • @paulsm4 I tested it by stepping through the code in a debugger and identifying e.g. memmove operations as noted, which doesn't translate well to a StackOverflow question, though I could've admitted as much. – Daniel Jennings Oct 12 '21 at 20:21
  • @Brian Thanks, I meant to tag this with STD which has been helpfully fixed already :) – Daniel Jennings Oct 12 '21 at 20:31

1 Answers1

5

In C++17, this was specified as:

basic_string& operator=(const charT* s);

Returns: *this = basic_string(s).

Remarks: Uses traits::length().

This sequence of operations guarantees that a copy is made before the original storage is destroyed. While a standard library is not required to implement this call using this exact sequence of operations, it is required to have the same behavior as described.

In C++20, this wording was reworked, but I would be surprised if the meaning was changed.

Marshall Clow
  • 15,972
  • 2
  • 29
  • 45
  • 1
    This suggests an alternate form of the assignment that is always guaranteed to work, even if the standard changes: `strCool = std::move(std::string(pszCool));` – Mark Ransom Oct 12 '21 at 00:58
  • Yes, that would work, but my point is that it is already guaranteed to work. – Marshall Clow Oct 12 '21 at 01:16