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::string
s. 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
Constant Expression Initializer for Static Class Member of Type Double
How to Make a Variadic MACro for Std::Cout
Should I Pass an Std::Function by Const-Reference
How Is the C++ Exception Handling Runtime Implemented
Unsigned and Signed Comparison
Constexpr Initializing Static Member Using Static Function
How to Std::Move Objects Out of Functions? (C++11)
How to Specify Setprecision Rounding
How to Check If the Input Is a Valid Integer Without Any Other Chars
How Do Compilers Treat Variable Length Arrays
Scope VS Life of Variable in C
Statically Declared 2-D Array C++ as Data Member of a Class
How to Use Parameters for the Table Name in SQLite3
Understanding Return Value Optimization and Returning Temporaries - C++