A String Literal is not a string


The other day, I discovered a bug in some code that can be simplified to something like this:

A library of functions that handles a lot of different types:

void doSomethingWith(int i)           { cout << "int"    << endl; };
void doSomethingWith(double d)        { cout << "double" << endl; };
void doSomethingWith(const string& s) { cout << "string" << endl; };
void doSomethingWith(const MyType& m) { cout << "MyType" << endl; };

Used like this:

    doSomethingWith(3);
    doSomethingWith("foo");

It of course outputs:

int
string

Then someone wanted to handle void pointers as well, and added this function to the library:

void doSomethingWith(const void* i) { cout << "void*" << endl; };

What is the output now? Make up your mind before looking.

int
void*

What happened? Why did C++ decide to use the const void * function instead of const string& that we wanted it to use?

The type of a string literal is not string, but const char[]. When deciding on the overloaded function to use, C++ will first see if any of them can be used directly. A const void* is the only type in our example than can point directly to the const char[], so that one is picked.

Before that function was introduced, none of the functions could be used directly, as neither const string& nor const MyType& can refer to a const char[], and it cannot be cast to an int or a double. C++ then looked for implicit constructors that could convert the const char[] into a usable type, and found std::string::string(const char * s). It then went on to create a temporary std::string object, and passed a reference to this object to void doSomethingWith(const string& s), like this:

doSomethingWith(std::string("foo"))

But then, when the const void* version appeared as an alternative, it preferred to use that one instead as it could be used without constructing any temporary objects.

As usual, the code for this blog post is available on GitHub.

If you enjoyed this post, you can subscribe to my blog, or follow me on Twitter.

6 thoughts on “A String Literal is not a string

  1. While this type of problem might occur in C#, you wouldn’t need to in this case since it has rich built-in support for working with types. You have `System.Object.GetType()`, the `typeof`, the `is` and the `as` keyword as well as `System.Type.GetTypeCode()` that makes working with types quite enjoyable.

    1. Nice! We have the typeid operator. If you have a pointer to Base pointing to an object of Derived, it will return Derived, but only if one of the methods in Base is declared as virtual, otherwise it returns Base.

      We also have C-style cast, dynamic_cast, static_cast and then of course reinterpret_cast which can cast between entirely unrelated pointers. MyClass obj; double* dp = reinterpret_cast<double*>(&c);? No problem!

      As you can see, working with types in C++ is also a lot of fun! :)

      1. It’s fun for sure, but for a whole bucketload of different reasons! Personally, I hate the flexibility of C++, especially since the “safety” of the compiler doesn’t really make me safe from anything and at the same time adds the inflexibility of static typed languages that require compilation. :)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s