21

The different construction syntaxes in C++ have always confused me a bit. In another question, it was suggested to try initializing a string like so

std::string foo{ '\0' };

This works and produces the intended result: a string of length 1 containing only the null character. In testing the code, I accidentally typed

std::string foo('\0');

This compiles fine (no warnings even with -Wall), but terminates at runtime with

terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_M_construct null not valid
Aborted (core dumped)

Now, as far as I can tell, there is no constructor for std::string which takes a single character as an argument, and this hypothesis is further confirmed when I attempt to pass the character indirectly.

char b = '\0';
std::string a(b);

This produces a nice, lengthy compile error. As does this

std::string a('z');

So my question is: what allows std::string a('\0'); to compile, and what makes it different from std::string a{ '\0' };?


Footnote: Compiling using g++ on Ubuntu. This doesn't strike me as a compiler bug, but just in case...

Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
  • 2
    My wild guess is that this '\0' gets interpreted as 0, and 0 is further thought to be nullptr. "null not valid" seems to back that theory. That said, I'd expect this to at least throw a warning. – CookiePLMonster Jan 15 '18 at 00:13
  • 15
    For better or worse, `\0` is a [null pointer constant](http://en.cppreference.com/w/cpp/types/NULL), so `std::string foo('\0');` is equivalent to `std::string foo(NULL);` and calls a constructor taking `char*`. But that constructor doesn't expect `NULL` - passing it exhibits undefined behavior. – Igor Tandetnik Jan 15 '18 at 00:15
  • 2
    Note that since C++14, `'\0'` is not a null pointer constant anymore, so you should get a compile error. – Brian Bi Jan 15 '18 at 05:29
  • @Brian Hm... that would be very nice. But even when compiling with `-std=c++14` or `-std=c++17`, it still compiles without an error. Perhaps `g++` hasn't fully implemented the new standard yet. – Silvio Mayolo Jan 15 '18 at 05:47
  • 2
    [g++ 7 rejects this code.](https://wandbox.org/permlink/zN06TRJCX0XAW5CI) – cpplearner Jan 15 '18 at 05:57

2 Answers2

20

Character '\0' is implicitly convertible to integer value of 0 thus representing implementation-defined null pointer constant. This:

std::string foo('\0');

calls a constructor overload accepting pointer of type const char* as a parameter and results in undefined behavior. It is equivalent to passing 0 or NULL:

std::string foo(0); // UB
std::string bar(NULL); // UB

The reference for the 4th and 5th constructor overloads states:

The behavior is undefined if s... including the case when s is a null pointer.

The second statement:

std::string foo{'\0'}; // OK

calls a constructor accepting std::initializer_list<char> as a parameter and does not cause UB.

You could call the constructor overload accepting count number of chars instead:

std::string s(1, '\0');
Ron
  • 14,674
  • 4
  • 34
  • 47
0

With C++ 14 or C++17 or C++11, this undefined behavior results in a compile error in both clang5.0 and gcc7.2.

#include<string> 
std::string S('\0');

error: no matching function for call to 'std::__cxx11::basic_string::basic_string(char)' std::string S('\0'); ^

The UB is fixed(to give a compiler error) in recent compiler versions.

Sitesh
  • 1,816
  • 1
  • 18
  • 25