Why do C++ streams treat bools as numerics?

Viewed 109

In C++, the stream I/O libraries do not by default print "true" or "false" when you output a bool, but rather "1" or "0". Instead, one is required to explicitly request a word-based printing, via std::boolalpha/std::noboolalpha:

#include <iostream>

int main()
{
    std::cout << bool{ false }; //Prints "0"
    std::cout << std::boolalpha; //Print all bools with alphanumerics
    std::cout << bool{ false }; //Prints "false"
    std::cout << std::noboolalpha; //Return to default
}

Leaving aside whether this is appropriate as a default (it's definitely confusing for beginners), I'm not sure why C++ offers the bool-as-numeric functionality at all. Some plausible reasons I can think of:

  • The mess of standardizing bool between C89, C99, and C++, might have meant that an overload for operator<< could not distinguish between bools and ints,
  • Primitive localization support ("true" in English is not the same as "Wahr" in German, but "1" is "1" everywhere...that uses Arabic numerals), or just
  • The "output parameter" problem when serializing to disk (std::ofstream{"filename"} << static_cast<int>(my_bool_variable); isn't particularly onerous, but the input analogue requires a temporary variable.)

In any case, I can't find the actual (historical) reason on the web; for example, the topic is never address in the C++FAQ, for example. So: Why does C++ offer this bool-as-numeric functionality in its input/output streams?

1 Answers

std::cout << bool{b} uses std::use_facet<std::num_put<char>>(std::cout.getloc()).put(std::cout, std::cout, std::cout.fill(), bool{b}) to do the actual output.

What this does is basically make a printf specifier and print that (modified by flags).

This is speculation, but it might be because in C++98 when this was originally standardised (and probably in pre-standard C++), the boolean type in C was int, so printf("%d", 0 == 0); output 1. (This is still the case with _Bool which promotes to int). So C++ wanted to treat bools as numeric types in this specific case (Especially since it uses a numeric facet of the locale), and just use the printf specifier of %d, to match what would be output in the corresponding C code.

Note that std::cout << std::setfill('0') << std::setw(5) << true; outputs 00001 (possibly even with commas/spaces/full stops depending on the locale). Without std::boolalpha, std::cout << bool{b} is literally equivalent to std::cout << static_cast<int>(bool{b}), just like the C variadic argument promotions.

Also, localisation support is available by modifying the std::numpunct facet of the locale and overriding std::numpunct<CharT>::do_truename and do_falsename. But this is orthogonal to whether the default behaviour should be std::boolalpha.

Related