2

I was working on one my projects, and while I was making the constructor for a class, I was setting some of my variables to a default value. I went to set an std::string to NULL, and it gave me an error. But when I define an std::string and set it to NULL on the same line, it works without any errors. I was wondering why

First Example

std::string text = NULL;

worked, and

Second Example

std::string text;
text = NULL;

didn't work. Now I know you shouldn't set a string to NULL, or 0, but I found this on accident.

Does the first example call a constructor that takes a char*, and thinking that 0 is a pointer to a char? I thought = called a constructor too, so I don't get why they wouldn't both work, unless std::string specifically overloads the = operator.

I am using Microsoft Visual Studio Express 2013

Greg M
  • 954
  • 1
  • 8
  • 20

2 Answers2

4

Value of NULL evaluates to 0.

Now, std::string text = NULL; will call constructor taking const char* and try to copy the data, but since NULL doesn't point to anything, an error will occur at run-time, I am getting (with gcc 5.2) :

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

Since, there is no operator= defined for std::string and int(or size_t), text = NULL; will not compile.

0x6773
  • 1,116
  • 1
  • 14
  • 33
  • But doesn't the `=` call the `size_t` ctor again? – Greg M Nov 14 '15 at 07:33
  • 1
    @GregM It calls the assignment operator. It's a bit confusing but using operator= in the context of initialization invokes an implicit constructor, while doing it post-initialization invokes an assignment operator. –  Nov 14 '15 at 07:34
  • @Ike @Ike I thought this, so I tested it out. I made a class `Test` and had a constructor take an int. When I did `Test t = 5`, it called the constructor, and `t = 10` also called the ctor. Wouldn't `t = 10` call the assignment operator? – Greg M Nov 14 '15 at 07:48
  • 1
    It does, but where it gets hairy is implicit construction. For example, if you have implicit: `Foo(int x)`, and no overloaded assignment operator for int, then trying to do: `Foo f; f = 123;` ... will actually call the parameterized constructor for Foo to implicitly construct it, and then the implicitly-generated assignment `Foo& operator=(const Foo&);` –  Nov 14 '15 at 07:51
  • So in those kinds of cases, it's first constructing `Foo` from 123 (implicitly behind your back), and then calling the copy assignment operator for `const Foo&`. In that kind of case with an implicit constructor and no overloaded `operator=` for the operand type, it's calling *both* the constructor and assignment operator. –  Nov 14 '15 at 07:54
  • @Ike so it's creating a temporary Foo and copying it? Kind of like doing `Foo f = Foo (123);`? – Greg M Nov 14 '15 at 08:06
  • 1
    @GregM: Basically, yes. Performance-wise, the compiler can do shortcuts as long as the semantics don't change (i.e. creating the "temporary" in-place, or -- since C++11 -- move-constructing it.) The idea is, since there is no way to *assign* an `int` to `Foo`, the compiler looks for alternatives, and finds that it's possible to assign a `Foo` to `Foo` *and* possible to *construct* a `Foo` from `int`, so that's what it does. – DevSolar Nov 14 '15 at 08:12
  • 1
    @GregM you can check if copy constructor is also used by deleting copy constructor, and checking if there are any errors. See http://ideone.com/nLke48 – 0x6773 Nov 14 '15 at 08:15
  • `Since, there is no operator= defined for std::string and int` not actually true, it not compiled because of there are two possible `operator=` `operator=(char)` and `operator=(const char *)` – fghj Nov 14 '15 at 18:40
  • @0x6773 "*since NULL doesn't point to anything, an error will occur at run-time*" - that is not guaranteed. Technically, passing `NULL` to the `std::string` constructor is *undefined behavior*, so anything could happen, including a crash – Remy Lebeau Oct 09 '21 at 01:40
1

The reason the 2nd assignment

text = NULL;

is in the VC++ compiler error message. It says:

error C2593: 'operator =' is ambiguous could be 'std::basic_string<char,std::char_traits,std::allocator> &std::basic_string<char,std::char_traits,std::allocator>::operator =(const _Elem)' or 'std::basic_string<char,std::char_traits,std::allocator> &std::basic_string<char,std::char_traits,std::allocator>::operator =(const _Elem *const )'

Breaking it down a bit, that says:

error C2593: 'operator =' is ambiguous could be 'std::string &std::string::operator =(const char)' or 'std::string &std::string::operator =(const char *const )'

So it doesn't work is because the compiler isn't sure whether 0 is meant to be a char value, or const char*.

It will compile if you use single quotes around the 0 to turn it into a char value:

text = '\0'; // NULL or can use any other character

Or if you cast it to a pointer type

type = (const char*)NULL; // Crashes, but now we're sure what type 0 is meant to be

that would compile also. Keep in mind assigning the std::string to a NULL pointer will crash as it tries to copy characters from NULL.

bobobobo
  • 64,917
  • 62
  • 258
  • 363
  • 1
    Along those lines, the reason the 1st constructor works is because there is no constructor that takes a single `char`, but there is one that takes a `const char*`, so passing `NULL` to the constructor is not ambiguous (just *undefined behavior*). – Remy Lebeau Oct 09 '21 at 01:38
  • Yes, the `std::string` [fill constructor](https://stackoverflow.com/a/4728725/) that accepts `char` also requires an `int` argument, for how many of that `char` to use (it is `std::string(int, char)` – bobobobo Oct 09 '21 at 15:49