为什么 std::(i)ostream 将有符号/无符号字符视为文本而不是整数



这段代码没有做它应该做的事情:

#include <iostream>
#include <cstdint>
int main()
{
    uint8_t small_integer;
    std::cin >> small_integer;
    std::cout << small_integer;
}

原因很简单:uint8_tunsigned char 的 typedef,流将此类型视为文本:
视觉C++ 2015 实施

template<class _Traits> inline
    basic_istream<char, _Traits>& operator>>(
        basic_istream<char, _Traits>& _Istr, unsigned char& _Ch)
    {    // extract an unsigned char
    return (_Istr >> (char&)_Ch);
    }

以及一个类似的代码,用于operator <<的 cast to char.

我的问题:

  1. 标准是否需要此行为(流式处理运算符将有符号/无符号字符视为字符类型而不是整数)?如果是,则:
    1. 这种违反直觉的语义背后的理由是什么?
    2. 这是否应被视为缺陷,是否有更改此语义的建议?

我可能应该补充一点解释,为什么我认为这是违反直觉的。尽管类型名称包含单词 char,但 signedunsigned 部分指定特定的整数语义,这些类型通常用作字节大小的整数。甚至标准也通过它们定义了int8_t/uint8_t

UPD:问题是关于unsigned charsigned char的流运营商重载的行为。

标准 (n3797) 如下所述:

27.7.2.2.3 basic_istream::运算符>>

template<class charT, class traits> 
basic_istream<charT,traits>& operator>>(basic_istream<charT,traits>& in, charT& c);
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);

12 效果:行为类似于 in 的格式化输入成员(如 27.7.2.2.1 中所述)。构造哨兵对象后,将从 in 中提取一个字符(如果有),并将其存储在 c 中。否则,函数将调用 in.setstate(failbit)。

27.7.3.6.4 字符插入器功能模板

// specialization 
template<class traits> 
basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, char c); 
// signed and unsigned 
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); 
1

效果:表现为输出的格式化输出函数 ( 27.7.3.6.1)。构造字符序列序列。如果 c 具有类型 char 并且流的字符类型不是 char,则 seq 由 out.widen(c) 组成;否则 seq 由 c 组成。确定 seq 的填充,如 27.7.3.6.1 中所述。将顺序插入到输出中。调用 os.width(0)。

所以第一个问题的答案:是的,标准要求operator >>operator <<的行为完全相同 charunsigned charsigned char ,即它们读取/写入单个字符,而不是整数。不幸的是,标准没有解释原因。我希望有人能阐明 2 和 3。

  1. 标准是否要求此行为?如果是,则:

你已经回答了这个问题。是的,该标准定义了 iostream 应如何处理有符号和无符号字符。

  1. 这种违反直觉的语义背后的理由是什么?

因为signed charunsigned char字符类型,所以它们总是被iostreams类视为字符。

线索在名称中:signed char是有符号的字符类型。 unsigned char是无符号字符类型。 其他整型在其名称中具有int(即使有时是可选的,例如 shortlong unsigned 分别与 short intlong unsigned int 相同)。

标准不需要说明为什么这是真的,因为它不是设计文档或 C 和 C++ 历史的基本原理,而是一个规范。

如果你想要一个行为类似于只有 8 位的整数的类型,那么你需要创建自己的类型(例如,使用枚举类型或保存值的结构)并定义相关的运算符重载。

  1. 这是否应被视为缺陷,是否有更改此语义的建议?

不,我不这么认为。它们一直是字符类型,更改它会导致太多代码。

最新更新