Initializing std::string
from a NULL
char pointer is undefined behaviour, I believe. So, here are alternative versions of a constructor, where mStdString
is a member variable of type std::string
:
void MyClass::MyClass(const char *cstr) :
mStdString( cstr ? cstr : "")
{}
void MyClass::MyClass(const char *cstr) :
mStdString(cstr ? std::string(cstr) : std::string())
{}
void MyClass::MyClass(const char *cstr)
{
if (cstr) mStdString = cstr;
// else keep default-constructed mStdString
}
Edit, constructor declaration inside class MyClass
:
MyClass(const char *cstr = NULL);
Which of these, or possibly something else, is the best or most proper way to initialize std::string
from a possibly NULL
pointer, and why? Is it different for different C++ standards? Assume normal release build optimization flags.
I'm looking for an answer with explanation of why a way is the right way, or an answer with a reference link (this also applies if answer is "doesn't matter"), not just personal opinions (but if you must, at least make it just a comment).
First thing first, you're right, from http://www.cplusplus.com/reference/string/string/string/:
Also, it depends on what a NULL pointer means for you. I assume its the same than an empty string for you.
I would go with the first one, because its the one I read best. First solution and second are the same. Third one wouldn't work if your string was
const
.Assuming you're happy with
cstr == NULL
yielding an emptymStdString
, I think the first one is probably the best.If nothing else, the third option you provide doesn't work if
mStdString
isconst
. The middle option benefits from "move semantics" under C++11, but is less obviously optimal or reasonable.So, my vote goes with the first option.
Whilst this may not REALLY be an answer (especially as you formulated the question) - but it's too long to fit as a comment and has code in it which doesn't ork in comments. I fully expect to get downvoted and have to delete this post - but I feel compelled to say something.
WHY would the initialization
char *
be NULL - and if so, couldn't you push it to the caller to make some sense of that situation - such as passing an empty string, or"unknown"
or"(null)"
as appropriate.In other words, something like this:
(There is probably some clever way to do this in an initializer list, but I can't be bothered to figure out how to correctly do that).
I for one is not keen on NULL as an input to a string parameter in any other way than "This really doesn't exist" - and if that's what you are actually trying to replicate, then you should have a
boolean
to say "doesn't exist", or a pointer to astd::string
that can be NULL if no string is present.The last one is silly because it doesn't use initialization when it could.
The first two are completely identical semantically (think of the
c_str()
member function), so prefer the first version because it is the most direct and idiomatic, and easiest to read.(There would be a semantic difference if
std::string
had aconstexpr
default constructor, but it doesn't. Still, it's possible thatstd::string()
is different fromstd::string("")
, but I don't know any implementations that do this, since it doesn't seem to make a lot of sense. On the other hand, popular small-string optimizations nowadays mean that both versions will probably not perform any dynamic allocation.)Update: As @Jonathan points out, the two string constructors will probably execute different code, and if that matters to you (though it really shouldn't), you might consider a fourth version:
Both readable and default-constructing.
Second update: But prefer
cstr ? cstr : ""
. As you can see below, when both branches call the same constructor, this can be implemented very efficiently using conditional moves and no branches. (So the two versions do indeed generate different code, but the first one is better.)For giggles, I've run both versions through Clang 3.3, with
-O3
, on x86_64, for astruct foo;
like yours and a functionfoo bar(char const * p) { return p; }
:Default constructor (
std::string()
):Empty-string constructor (
""
):In my case, it would even appear that
""
generates better code: Both versions callstrlen
, but the empty-string version doesn't use any jumps, only conditional moves (since the same constructor is called, just with two different arguments). Of course that's a completely meaningless, non-portable and non-transferable observation, but it just goes to show that the compiler doesn't always need as much help as you might think. Just write the code that looks best.