Last Update:
[Quick Case] Surprising Conversions of const char* to bool
Table of Contents
If you have two function overloads foo()
: one is taking const std::string&
and the other taking bool
. Which one of them will be selected when you call foo("hello world");
?
Let’s see where such a case might bite us and cause troubles?
Intro
Here’s the example once again
void foo(const std::string& in) { std::cout << in << '\n'; }
void foo(bool in) { std::cout << "bool: " << in << '\n';}
foo("Hello World");
What’s the output?
.
.
.
bool: true
And why is that?
Let’s see the standard:
C++17 Draft: Boolean conversions, conv.bool:
A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true….
In other words, when performing a function overload resolution lookup the compiler doesn’t have a function that exactly matches the input parameter (it’s an array of const char
), so it has to perform a conversion.
We have two options: convert to bool
or convert to a user defined type (std::string
is still a custom type).
Both conversion produce a viable function, but (from cppreference):
A standard conversion sequence is always better than a user-defined conversion sequence
That’s why the bool
conversion is selected.
Of course, the conversion of a pointer to bool
is not always causing troubles. For example, you can write:
if (myPTR) { }
(assuming myPTR
is a pointer)
A Case with std::variant
Another unwanted scenario that might happen is when you have a std::variant
with bools and strings. The same conversion to pointer and to bool might happen.
Have a look:
std::variant<std::string, bool, int> var { 42 };
var = "Hello World";
Initially, the variant will have the active type of int
, but then you assign a string literal… so it will convert to bool
, not to std::string
.
Same thing can happen when you initialise a variant with const char*
:
std::variant<std::string, bool, int> var { "Hello World" };
Fortunately, such unwanted conversions it about to be fixed for a variant. You can check GCC trunk (10.0) which implements already this C++17 fix: A sane variant converting constructor - P0608.
The paper adds additional enforcements on the constructor and the assignment operator:
Quoting the part from std::variant
variant<float, long, double> v = 0;
Before the fix, this line won’t compile (we have several narrowing conversions possible), but after the improvement, it will hold long
.
Here’s a commit for libstdc++ that implements that change:
Implement sane variant converting constructor (P0608R3)
Summary
In this short blog post, I wanted to tackle an issue of unwanted conversions that might happen when selection a function overload, or an alternative type in std::variant
. Although for custom types like variant the library implementations can fix unwanted conversions it’s still good to keep your types as much “consistent” as possible so that you limit the number of conversions.
You can play with code at @Wandbox and switch between compilers, GCC 9.1 and GCC 10.0 to see the difference
Have you encountered some “unwanted” conversions that cause bugs in your code? Please share your stories in comments.
I've prepared a valuable bonus for you!
Learn all major features of recent C++ Standards on my Reference Cards!
Check it out here: