I want to create a set of methods that will output a value with special formatting based on its type. When I'm doing it this way, it looks so far so good:
static void printValue(std::ostringstream& out, int value) {
out << value;
}
static void printValue(std::ostringstream& out, double value) {
out << value;
}
static void printValue(std::ostringstream& out, const std::string& value) {
out << "\"" << escapeString(value) << "\"";
}
Testing:
printValue(std::cout, 123); // => 123
printValue(std::cout, 3.14); // => 3.14
printValue(std::cout, "foo"); // => "foo"
However, as soon as I add bool
overload:
static void printValue(std::ostringstream& out, bool value) {
out << (value ? "true" : "false");
}
... things break, as bool
-based overload seems to be used by default by add string invocations:
printValue(std::cout, 123); // => 123
printValue(std::cout, 3.14); // => 3.14
printValue(std::cout, true); // => true
printValue(std::cout, "foo"); // => true <= !!!
Is there any way I can escape this automatic cast-to-bool and force compiler to choose a correct method for strings?
You could add an template overload that takes a reference to array of char
of size N
where N
is a template parameter, and/or one that accepts a const char*
.
template <std::size_t N>
static void printValue(sts::ostringstream& out, const char (&str)[N])
{
out << str;
}
Why this happens:
When the compiler is trying to choose the "best" function to call given some arguments, it prioritizes the overload set in roughly this way:
- Exact match: For example, if you pass a
const char*
and the function accepts a const char*
, that's great
- Standard conversion sequences: For example, converting a
const char[4]
to a const char*
and then a const char*
to bool
(based on a check for nullptr
). Or promoting a short
to an int
- These conversions also have their own prioritization that we can ignore for the sake of brevity
- User defined conversions: basically, conversion to some non-fundamental type (via constructor or conversion operator)
Because you are providing a character literal "foo"
it has a standard conversion sequence from const char[4]
to const char*
(via array to pointer conversion, see [conv.array]) and then from const char*
to bool
via boolean conversion (see [conv.bool]).
So, while it is possible to construct a const std::string&
with "foo"
via a user defined conversion, such a conversion is of lower precedence than the standard conversion sequence, and thus the boolean is chosen.
What you can do:
- Anticipate this and write an overload for
const char*
or const char[N]
like @juanchopanza said
- create a
std::string
when you call the function: printValue(std::string{"foo"});