I'm actually surprised you got the streams instantiated to do any reading at all! What the result will be is possibly implementation defined (i.e., you might find the behavior described in the compiler's documentation) but possibly it is just not specified (although not quite undefined). I don't think the stream classes are required to support instantiations for other types than char
and wchar_t
immediately, i.e., without the user providing at least some of the facets.
The standard stream classes are templates on the character type but aren't easy to instantiate for any unsupported type. At bare minimum, you'd need to implement a suitable std::codecvt<int16_t, char, std::mbstate_t>
facet converting between the external representation in byte and the internal representation. From the looks of it the two systems you tried have made different choices for their default implementation.
std::codecvt<internT, externT, stateT>
is the facet used to convert between an external representation of characters and an internal representation of characters. Streams are only required to support char
which is considered to represent bytes as the external type externT
. The internal character type internT
can be any integral type but the conversion needs to be defined by implementing the code conversion facet. If I recall correctly, the streams can also assume that the state type stateT
is std::mbstate_t
(which is actually somewhat problematic because there is no interface defined for this type!).
Unless you are really dedicated in creating an I/O stream for your character type uint16_t
, you probably want to read bytes using std::ifstream
and convert them to your character type. Similarly for writing characters. To really create an I/O stream also supporting formatting, you'd need a number of other facets, too (e.g., std::ctype<uint16_t>
, std::num_punct<uint16_t>
) and you'd need to build a std::locale
to contain all of these plus a few which can be instantiated from the standard library's implementation (e.g., std::num_get<uint16_t>
and std::num_put<uint16_t>
; I think their iterator types are suitable defaulted).