117. basic_ostream uses nonexistent num_put member functions

Section: 30.7.5.2.2 [ostream.inserters.arithmetic] Status: CD1 Submitter: Matt Austern Opened: 1998-11-20 Last modified: 2016-02-10

Priority: Not Prioritized

View all other issues in [ostream.inserters.arithmetic].

View all issues with CD1 status.

Discussion:

The effects clause for numeric inserters says that insertion of a value x, whose type is either bool, short, unsigned short, int, unsigned int, long, unsigned long, float, double, long double, or const void*, is delegated to num_put, and that insertion is performed as if through the following code fragment:

bool failed = use_facet<
   num_put<charT,ostreambuf_iterator<charT,traits> > 
   >(getloc()).put(*this, *this, fill(), val). failed();

This doesn't work, because num_put<>::put is only overloaded for the types bool, long, unsigned long, double, long double, and const void*. That is, the code fragment in the standard is incorrect (it is diagnosed as ambiguous at compile time) for the types short, unsigned short, int, unsigned int, and float.

We must either add new member functions to num_put, or else change the description in ostream so that it only calls functions that are actually there. I prefer the latter.

Proposed resolution:

Replace 27.6.2.5.2, paragraph 1 with the following:

The classes num_get<> and num_put<> handle locale-dependent numeric formatting and parsing. These inserter functions use the imbued locale value to perform numeric formatting. When val is of type bool, long, unsigned long, double, long double, or const void*, the formatting conversion occurs as if it performed the following code fragment:

bool failed = use_facet<
   num_put<charT,ostreambuf_iterator<charT,traits> >
   >(getloc()).put(*this, *this, fill(), val). failed();

When val is of type short the formatting conversion occurs as if it performed the following code fragment:

ios_base::fmtflags baseflags = ios_base::flags() & ios_base::basefield;
bool failed = use_facet<
   num_put<charT,ostreambuf_iterator<charT,traits> >
   >(getloc()).put(*this, *this, fill(),
      baseflags == ios_base::oct || baseflags == ios_base::hex
         ? static_cast<long>(static_cast<unsigned short>(val))
         : static_cast<long>(val)). failed();

When val is of type int the formatting conversion occurs as if it performed the following code fragment:

ios_base::fmtflags baseflags = ios_base::flags() & ios_base::basefield;
bool failed = use_facet<
   num_put<charT,ostreambuf_iterator<charT,traits> >
   >(getloc()).put(*this, *this, fill(),
      baseflags == ios_base::oct || baseflags == ios_base::hex
         ? static_cast<long>(static_cast<unsigned int>(val))
         : static_cast<long>(val)). failed();

When val is of type unsigned short or unsigned int the formatting conversion occurs as if it performed the following code fragment:

bool failed = use_facet<
   num_put<charT,ostreambuf_iterator<charT,traits> >
   >(getloc()).put(*this, *this, fill(), static_cast<unsigned long>(val)).
failed();

When val is of type float the formatting conversion occurs as if it performed the following code fragment:

bool failed = use_facet<
   num_put<charT,ostreambuf_iterator<charT,traits> >
   >(getloc()).put(*this, *this, fill(), static_cast<double>(val)).
failed();

[post-Toronto: This differs from the previous proposed resolution; PJP provided the new wording. The differences are in signed short and int output.]

Rationale:

The original proposed resolution was to cast int and short to long, unsigned int and unsigned short to unsigned long, and float to double, thus ensuring that we don't try to use nonexistent num_put<> member functions. The current proposed resolution is more complicated, but gives more expected results for hex and octal output of signed short and signed int. (On a system with 16-bit short, for example, printing short(-1) in hex format should yield 0xffff.)