while I was reading nVidia CUDA source code, I stumbled upon these two lines:
std::string stdDevString;
stdDevString = std::string(device_string);
Note that device_string is a char[1024]. The question is: Why construct an empty std::string, then construct it again with a C string as an argument? Why didn’t they call std::string stdDevString = std::string(device_string); in just one line?
Is there a hidden string initialization behavior that this code tries to evade/use? Is to ensure that the C string inside stdDevString remains null terminated no matter what? Because as far as I know, initializing an std::string to a C string that’s not null terminated will still exhibit problems.
No good reason for what they did. Given the
std::string::string(const char*)constructor, you can simply use any of:The two-step default construction then assignment is just (bad) programmer style or oversight. Sans optimisation, it does do a little unnecessary construction, but that’s still pretty cheap. It’s likely removed by optimisation. Not a biggie – I doubt if I’d bother to mention it in a code review unless it was in an extremely performance sensitive area, but it’s definitely best to defer declaring variables until a useful initial value is available to construct them with, localising it all in one place: not only is it less error prone and cross-referenceable, but it minimises the scope of the variable simplifying the reasoning about its use.
No – it made no difference to that. Since C++11 the internal buffer in
stdDevStringwould be kept NUL terminated regardless of which constructor is used, while for C++03 isn’t not necessarily terminated – see dedicated heading for C++03 details below – but there’s no guarantees regardless of how construction / assignment is done.You’re right – any of the construction options you’ve listed will only copy ASCIIZ text into the
std::string– considering the first NUL ('\0') the terminator. If the char array isn’t NUL-terminated there will be problems.(That’s a separate issue to whether the buffer inside the
std::stringis kept NUL terminated – discussed above).Note that there’s a separate
string(const char*, size_type)constructor that can create strings with embedded NULs, and won’t try to read further than told (Constructor (4) here)C++03 std::strings were not guaranteed NUL-terminated internally
Whichever way the
std::stringis constructed and initialised, before C++11 the Standard did not require it to be NUL-terminated within the string’s buffer.std::stringwas best imagined as containing a bunch of potentially non-printable (loosely speaking, binary in the ftp/file I/O sense) characters starting at addressdata()and extending forsize()characters. So, if you had:Note that the std::string API requires
c_str()to return a pointer to a NUL-terminated value. To do so, it can either:NULon the end of the string buffer at all times (in which casedata[5]would happen to be safe on that implementation, but the code could break if the implementation changed or the code was ported to another Standard library implementation etc.)reactively wait until
c_str()is called, then:data()), append aNULand return the same pointer value thatdata()would returnNULterminate it, and return a pointer to it (typically but optionally this buffer would replace the old buffer which would be deleted, such that callingdata()immediately afterwards would return the same pointer returned byc_str())