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.