Assign a Nullptr to a Std::String Is Safe

Assign a nullptr to a std::string is safe?

Interesting little question. According to the C++11 standard, sect. 21.4.2.9,

basic_string(const charT* s, const Allocator& a = Allocator());

Requires: s shall not be a null pointer.

Since the standard does not ask the library to throw an exception when this particular requirement is not met, it would appear that passing a null pointer provoked undefined behavior.

Is it valid to pass nullptr to std::string::assign?

Pedantically, a nullptr does not meet the requirements of pointing to an array of size >=0, and therefore the standard does not guarantee the behaviour (it's UB).

On the other hand, the implementation wouldn't be allowed to dereference the pointer if n is zero, because the pointer could be to an array of size zero, and dereferencing such a pointer would have undefined behaviour. Besides, there wouldn't be any need to do so, because nothing is copied.

The above reasoning does not mean that it is OK to ignore the UB. But, if there is no reason to disallow s.assign(nullptr, 0) then it could be preferable to change the wording of the standard to "If n is greater than zero, then s points to ...". I don't know of any good reason to disallow it, but neither can I promise that a good reason doesn't exist.

Note that adding a check is hardly complicated:

s.assign(ptr ? ptr : "", n);

What is an array of zero characters

This is: new char[0]. Arrays of automatic or static storage may not have a zero size.

Why can you assign nullptr to std::string?

That's simply because there are constructors (number (5) in the link) and assignment operators (number (3) in the link) for std::string that accept a const char*, and hence the nullptr matches.

Before C++11 (and therefore before nullptr), the same problem occurred when you tried to construct from 0 or NULL. All those cases were illegal and result in undefined behaviour, although at least one STL (RogueWave?) accepted it in the past and generated an empty string.

Why doesn't std::string take a null pointer?

No good reason as far as I know.

Someone just proposed a change to this a month ago. I encourage you to support it.

std::string is not the best example of well done standardization. The version initially standardized was impossible to implement; the requirements placed on it where not consistent with each other.

At some point that inconsistency was fixed.

In c++11 the rules where changed that prevent COW (copy on write) implementations, which broke the ABI of existing reasonably compliant std::strings. This change may have been the point where the inconsistency was fixed, I do not recall.

Its API is different than the rest of std's container because it didn't come from the same pre-std STL.

Treating this legacy behavior of std::string as some kind of reasoned decision that takes into account performance costs is not realistic. If any such testing was done, it was 20+ years ago on a non-standard compliant std::string (because none could exist, the standard was inconsistent).

It continues to be UB on passing (char const*)0 and nullptr due to inertia, and will continue to do so until someone makes a proposal and demonstrates that the cost is tiny while the benefit is not.

Constructing a std::string from a literal char const[N] is already a low performance solution; you already have the size of the string at compile time and you drop it on the ground and then at runtime walk the buffer to find the '\0' character (unless optimized around; and if so, the null check is equally optimizable). The high performance solution involves knowing the length and telling std::string about it instead of copying from a '\0' terminated buffer.

Avoiding improper std::string initialization with NULL const char* using g++

I think it is actually undefined behavior and not checked by the compiler. You are lucky that this implementation throws an exception.

However, you can avoid such problems by specifying that you want default or zero-initialization in a type-agnostic way:

struct Foo
{
X id;
Foo() : id() {} //note empty parenthesis
};

Passing a nullptr to my overloaded function causes a runtime error

When using namespace bin; is commented out, there is only 1 version of f() available that can take a nullptr as input. nullptr is not implicitly convertible to int or double, so ns::f(int) and ns::f(double) are ruled out. But std::string can be constructed from a const char*, and nullptr is implicitly convertible to const char*, so the compiler can construct a temporary std::string object to pass to ns::f(std::string). However, it is undefined behavior to construct a std::string from a null const char*, hence the runtime error (which is NOT guaranteed, BTW, as the behavior is undefined, so anything can happen).

When using namespace bin; is not commented out, there are 2 versions of f() available that can take a nullptr as input. bin::f(int*) is a better match than ns::f(std::string), as nullptr is implicitly convertible to int*, so there is no need to construct a temporary object, thus the compiler chooses to call bin::f(int*) instead of ns::f(std::string).



Related Topics



Leave a reply



Submit