Let’s start with a small test. Is the equivalence expressed with the following assertion correct?

void test_length(std::string const& s) { assert(s.length() == strlen(s.c_str())); }

It is not; otherwise I wouldn’t be mentioning this in the post; but do you know why it is wrong?

If you have run a couple of tests and observed that the assertion doesn’t fail, you may get convinced that it is just fine. And it is so for some strings, but definitely not for every string.

The crux is in how the length of the string is computed. In C you count characters until you reach the first null character. This is the same in C++ for types like const char* . For std::string the length of the string is tracked separately and is independent of the contained characters. You can have as many null characters as you like and they do not affect the string length! (Well, they do affect the string length in the sense that every '\0' adds 1 to the length, but it does not indicate the end of the string.)

Surprised? You might be, because a std::string is often initialized from a null-terminated character string and often its value is used as a null-terminated character string, when c_str is called; but nonetheless a std::string is not a null-terminated character string.

Is it possible to create a std::string with null characters inside? If you do this:

std::string s ("\0\0test");

The length of s is 0 . True, because you used the constructor that assumes a null-terminated character string input. Try one which explicitly specifies the length:

std::string s ("\0\0test", 6);

Now, its length is 6 , and our test_length will fail.

But why would anyone put a null character inside the string? Well, this is what a string is supposed to be: a sequence of characters, and 0 is just one more character. A string is not necessarily meant to be displayed. My colleague really encountered this problem. A raw buffer obtained from a socket was converted to a string and it had some header information in front, which contained zeros. Trying to log the content resulted in logging an empty null-text, even though it was obvious that some content was there.

Having this in mind helps understand why a numeric user-defined literal has this signature:

double operator "" _d (const char* value);

Whereas a string literal has signature:

std::string operator "" _s (const char* text, size_t len);

This is because, in case of a numeric literal, you will never get character '\0' , whereas someone might want to create a string with a null value inside:

test_length("one\0two"_s);