1. Motivation
#include <iostream>#include <format>int main () { // Prints: std :: cout << static_cast < char > ( 48 ) << '\n' // 0 << static_cast < signed char > ( 48 ) << '\n' // 0 << static_cast < unsigned char > ( 48 ) << '\n' // 0 << static_cast < int8_t > ( 48 ) << '\n' // 0 << static_cast < uint8_t > ( 48 ) << '\n' // 0 << static_cast < short > ( 48 ) << '\n' // 48 << std :: format ( "{}" , static_cast < char > ( 48 )) << '\n' // 0 << std :: format ( "{}" , static_cast < int8_t > ( 48 )) << '\n' // 48 << std :: format ( "{}" , static_cast < uint8_t > ( 48 )) << '\n' ; // 48 }
There are overloads for
for
,
that take an
, and a
.
In addition, there are overloads for
for
,
that take an
and an
.
These overloads are specified to have equivalent behavior to
the non-signedness qualified overloads: [istream.extractors] [ostream.inserters.character].
This is surprising. Per [basic.fundamental] p1 and p2:
There are five standard signed integer types: "
", "
signed char ", "
short int ", "
int ", and "
long int "... There may also be implementation-defined extended signed integer types. The standard and extended signed integer types are collectively called signed integer types.
long long int For each of the standard signed integer types, there exists a corresponding (but different) standard unsigned integer type: "
", "
unsigned char ", "
unsigned short int ", "
unsigned int ", and "
unsigned long int "... Likewise, for each of the extended signed integer types, there exists a corresponding extended unsigned integer types. The standard and extended unsigned integer types are collectively called unsigned integer types.
unsigned long long int
Thus,
and
should be treated as integers, not as characters.
This is highlighted by the fact, that
and
are specified to be aliases to (un)signed integer types,
which are in practice going to be
and
.
Note: The Solaris implementation is different, and defines
to be
by default.
This is not conformant.
and
are not character types.
Per [basic.fundamental] p11, since [P2314R4]:
The types
,
char ,
wchar_t ,
char8_t , and
char16_t are collectively called character types.
char32_t
and
are included in the set of ordinary character types and narrow character types ([basic.fundamental] p7),
but these definitions are used for specifying alignment, padding, and indeterminate values ([basic.indet]),
and are arguably not related to characters in the sense of pieces of text.
has already taken a step in the right direction here,
by treating
and
as integers.
It’s specified to not give special treatment to these types,
but to use the standard definitions of (un)signed integer type
to determine whether a type is to be treated as an integer when formatting.
This paper proposes that these overloads in iostreams should be deprecated.
2. Impact
It’s difficult to find examples where this is the sought-after behavior, and would become deprecated with this change. These snippets aren’t easily greppable.
It’s easy to find counter-examples, however, where workarounds have to be employed to insert or extract
s or
s
as integers. Some of them can be found with isocpp.org codesearch by searching for
or
, although false positives there are very prevalent.
/* ... */ << static_cast < int > ( my_schar );
These overloads have existed since C++98.
3. Wording
This wording is relative to [N4971].
3.1. Modify [istream.general] p1
// ... // [istream.extractors], character extraction templates template < class charT , class traits > basic_istream < charT , traits >& operator >> ( basic_istream < charT , traits >& , charT & ); template < class traits > basic_istream < char , traits >& operator >> ( basic_istream < char , traits >& , unsigned char & ); template < class traits > basic_istream < char , traits >& operator >> ( basic_istream < char , traits >& , signed char & ); template < class charT , class traits , size_t N > basic_istream < charT , traits >& operator >> ( basic_istream < charT , traits >& , charT ( & )[ N ]); template < class traits , size_t N > basic_istream < char , traits >& operator >> ( basic_istream < char , traits >& , unsigned char ( & )[ N ]); template < class traits , size_t N > basic_istream < char , traits >& operator >> ( basic_istream < char , traits >& , signed char ( & )[ N ]);
3.2. Modify [istream.extractors], around p7 to p12
template < class charT , class traits , size_t N > basic_istream < charT , traits >& operator >> ( basic_istream < charT , traits >& , charT ( & )[ N ]); template < class traits , size_t N > basic_istream < char , traits >& operator >> ( basic_istream < char , traits >& , unsigned char ( & )[ N ]); template < class traits , size_t N > basic_istream < char , traits >& operator >> ( basic_istream < char , traits >& , signed char ( & )[ N ]);
Effects: Behaves like a formatted input member (as described in [istream.formatted.reqmts]) of
.
After a sentry object is constructed,
extracts characters and stores them into
.
If
is greater than zero,
is
.
Otherwise
is
.
is the maximum number of characters stored.
Characters are extracted and stored until any of the following occurs:
-
characters are stored;n -1 -
end of file occurs on the input sequence;
-
letting
bect
,use_facet < ctype < charT >> ( in . getloc ())
isct . is ( ct . space , c ) true
.
then stores a null byte (
) in the next position, which may be the first position if no characters were extracted.
then calls
.
If the function extracted no characters,
is set in the input function’s local error state before
is called.
Returns:
.
template < class charT , class traits > basic_istream < charT , traits >& operator >> ( basic_istream < charT , traits >& , charT & ); template < class traits > basic_istream < char , traits >& operator >> ( basic_istream < char , traits >& , unsigned char & ); template < class traits > basic_istream < char , traits >& operator >> ( basic_istream < char , traits >& , signed char & );
Effects: Behaves like a formatted input member (as described in [istream.formatted.reqmts]) of
.
A character is extracted from
, if one is available, and stored in
.
Otherwise,
is set in the input function’s local error state before
is called.
Returns:
.
3.3. Modify [ostream.general] p1
// ... // [ostream.inserters.character], character inserters template < class charT , class traits > basic_ostream < charT , traits >& operator << ( basic_ostream < charT , traits >& , charT ); template < class charT , class traits > basic_ostream < charT , traits >& operator << ( basic_ostream < charT , traits >& , char ); template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , char ); template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , signed char ); template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , unsigned char ); template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , wchar_t ) = delete ; template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , char8_t ) = delete ; template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , char16_t ) = delete ; template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , char32_t ) = delete ; template < class traits > basic_ostream < wchar_t , traits >& operator << ( basic_ostream < wchar_t , traits >& , char8_t ) = delete ; template < class traits > basic_ostream < wchar_t , traits >& operator << ( basic_ostream < wchar_t , traits >& , char16_t ) = delete ; template < class traits > basic_ostream < wchar_t , traits >& operator << ( basic_ostream < wchar_t , traits >& , char32_t ) = delete ; template < class charT , class traits > basic_ostream < charT , traits >& operator << ( basic_ostream < charT , traits >& , const charT * ); template < class charT , class traits > basic_ostream < charT , traits >& operator << ( basic_ostream < charT , traits >& , const char * ); template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , const char * ); template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , const signed char * ); template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , const unsigned char * ); template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , const wchar_t * ) = delete ; template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , const char8_t * ) = delete ; template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , const char16_t * ) = delete ; template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , const char32_t * ) = delete ; template < class traits > basic_ostream < wchar_t , traits >& operator << ( basic_ostream < wchar_t , traits >& , const char8_t * ) = delete ; template < class traits > basic_ostream < wchar_t , traits >& operator << ( basic_ostream < wchar_t , traits >& , const char16_t * ) = delete ; template < class traits > basic_ostream < wchar_t , traits >& operator << ( basic_ostream < wchar_t , traits >& , const char32_t * ) = delete ; // ...
3.4. Modify [ostream.inserters.character]
template < class charT , class traits > basic_ostream < charT , traits >& operator << ( basic_ostream < charT , traits >& , charT ); template < class charT , class traits > basic_ostream < charT , traits >& operator << ( basic_ostream < charT , traits >& , char ); // specialization template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , char ); // signed and unsigned template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , signed char ); template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , unsigned char );
Effects: Behaves as a formatted output function of
.
Constructs a character sequence
.
If
has type
and the character container type of the stream is not
,
then
consists of
; otherwise
consists of
.
Determines padding for
as described in [ostream.formatted.reqmts].
Inserts
into
.
Calls
.
Returns:
.
template < class charT , class traits > basic_ostream < charT , traits >& operator << ( basic_ostream < charT , traits >& , const charT * ); template < class charT , class traits > basic_ostream < charT , traits >& operator << ( basic_ostream < charT , traits >& , const char * ); template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , const char * ); template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , const signed char * ); template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& , const unsigned char * );
Preconditions:
is not a null pointer.
Effects: Behaves like a formatted inserter (as described in [ostream.formatted.reqmts]) of
.
Creates a character sequence
of
characters starting at
, each widened using
([basic.ios.members]),
where
is the number that would be computed as if by:
-
for the overload where the first argument is of typetraits :: length ( s )
and the second is of typebasic_ostream < charT , traits >&
, and also for the overload where the first argument is of typeconst charT *
and the second is of typebasic_ostream < char , traits >&
,const char * -
for the overload where the first argument is of typechar_traits < char > :: length ( s )
and the second is of typebasic_ostream < charT , traits >&
.const char * , -
for the other two overloads.traits :: length ( reinterpret_cast < const char *> ( s ))
Determines padding for
as described in [ostream.formatted.reqmts].
Inserts
into
.
Calls
.
Returns:
.
3.5. Add a new subclause in Annex D after [depr.atomics]
Deprecated
and
extraction [depr.istream.extractors]
The following function overloads are declared in addition to those specified in [istream.extractors]:
template < class traits > basic_istream < char , traits >& operator >> ( basic_istream < char , traits >& in , unsigned char & c ); template < class traits > basic_istream < char , traits >& operator >> ( basic_istream < char , traits >& in , signed char & c );
Effects: Behaves like a formatted input member (as described in [istream.formatted.reqmts]) of
.
A character is extracted from
, if one is available, and stored in
.
Otherwise,
is set in the input function’s local error state before
is called.
Returns:
.
template < class traits , size_t N > basic_istream < char , traits >& operator >> ( basic_istream < char , traits >& in , unsigned char ( & )[ N ] s ); template < class traits , size_t N > basic_istream < char , traits >& operator >> ( basic_istream < char , traits >& in , signed char ( & )[ N ] s );
Effects: Behaves like a formatted input member (as described in [istream.formatted.reqmts]) of
.
After a sentry object is constructed,
extracts characters and stores them into
.
If
is greater than zero,
is
.
Otherwise
is
.
is the maximum number of characters stored.
Characters are extracted and stored until any of the following occurs:
-
characters are stored;n -1 -
end of file occurs on the input sequence;
-
letting
bect
,use_facet < ctype < charT >> ( in . getloc ())
isct . is ( ct . space , c ) true
.
then stores a null byte (
) in the next position, which may be the first position if no characters were extracted.
then calls
.
If the function extracted no characters,
is set in the input function’s local error state before
is called.
Returns:
.
3.6. Add a new subclause in Annex D after the above ([depr.istream.extractors])
Deprecated
and
insertion [depr.ostream.inserters]
The following function overloads are declared in addition to those specified in [ostream.inserters]:
template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& out , signed char c ); template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& out , unsigned char c );
Effects: Equivalent to:
.
template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& out , const signed char * s ); template < class traits > basic_ostream < char , traits >& operator << ( basic_ostream < char , traits >& out , const unsigned char * s );
Effects: Equivalent to:
.