Not sure. Probably because that's how C's "printf" behaves.
C++ inherits a lot from C; sometimes the only justification is
that if folks recompile their code with a C++ compiler they
don't get any surprises (at least most of the time). Now, C
doesn't have streams, but there is '#' flag character in the
'printf' formatting specification. The 'showbase' is defined
to mimic it.
That's definitely the reason. All C++ says about showbase is
that if the type is integral and showbase is set, formats "as
if" there were a # in the specifier to printf. (All of the
formatting is specified in terms of printf format specifiers.)
For #, the C standard says: "For x (or X) conversion, a nonzero
result has 0x (or 0X) prefixed to it."
The rationale for the C standard doesn't say anything about this
either.
(It surprised me, too, just a couple of days ago. In the end,
however, I wanted something like "0x1AB", with a small x but
capital hexadecimal digits, so I had to insert the 0x manually
anyway. And since I was using '0' as a fill character at that
point, inserting the 0x didn't require any jumping through
hoops. But I still think it strange.)
If you'd like to know more about justifications that were used
when designing formatted I/O for C++, ask in 'comp.std.c++',
they discuss the rationales behind the decisions to put this
or that in the Standard.
In this case, comp.std.c might be more appropriate. (Of course,
the answer might be simply that that's what all existing
implementations did at the time. And who knows why they did
it.)