【问题标题】:How standard C++ input output streams have to handle intXX_t types?标准 C++ 输入输出流必须如何处理 intXX_t 类型?
【发布时间】:2017-01-09 18:04:29
【问题描述】:

根据这个问题的答案Is overloading on all of the fundamental integer types is sufficient to capture all integers?所有基本类型的重载可能无法处理int8_tint64_t等类型。另一方面,根据文档std::ostream formatted outputstd::istream formatted input完全由重载所有基本类型。那么在int8_t 和其他人无法通过这种重载处理的平台上,C++ 流将如何处理它们?会不会编译失败?标准库实现者是否必须提供其他未记录的方法?还有什么?

【问题讨论】:

  • 我不明白你的问题。如果int8_t 在某个平台上不可用,std::ostream 和家人不可能重载它们。在那个平台上,int8_t 不存在,因此不是一个类型。
  • 如有必要,int8_t 始终可以提升为基本类型。如果您的平台有 32 位 long long,则可能有问题的是 int64_t
  • @Rakete1111 read question in link,问题不是int8_没有定义,而是它不是基本类型的别名。
  • @Useless 我怀疑提升是否能很好地处理重载。但即使是这样,它对std::istream的引用也无济于事
  • @Useless 你无法获得 32 位 long long。最低要求是 64 位。

标签: c++ c++11 language-lawyer


【解决方案1】:

对于整数类型,[istream] 定义:

basic_istream<charT,traits>& operator>>(bool& n);
basic_istream<charT,traits>& operator>>(short& n);
basic_istream<charT,traits>& operator>>(unsigned short& n);
basic_istream<charT,traits>& operator>>(int& n);
basic_istream<charT,traits>& operator>>(unsigned int& n);
basic_istream<charT,traits>& operator>>(long& n);
basic_istream<charT,traits>& operator>>(unsigned long& n);
basic_istream<charT,traits>& operator>>(long long& n);
basic_istream<charT,traits>& operator>>(unsigned long long& n);

如果x 是不在该列表中的整数类型,则is &gt;&gt; x 形式的任何代码都可能无法编译。

作为实现质量问题,提供扩展整数类型的实现可以为这些类型添加operator&gt;&gt; 重载。 [member.functions]/2 允许这样做(如 cmets 中的 hvd 所述),讨论标准库中的类:

一个实现可以在一个类中声明额外的非虚拟成员函数签名:

[...]

  • 通过为成员函数名称添加成员函数签名。

对 C++ 标准库中描述的成员函数签名的调用的行为就像实现声明没有附加成员函数签名一样。

这比一般规则更强有力的保证,即实现可以添加不会破坏符合程序的扩展。使用 SFINAE 的符合程序的行为可能会受到额外重载的影响。


对于输出,不在列表中的整数类型将隐式转换为列表中的另一个整数类型。

【讨论】:

  • “可以为这些类型添加 operator>> 重载。”这不违反标准吗?
  • @Slava:不。允许实现将任何其他功能添加到标准中,作为标准库扩展。只要它们不改变规范边界之外的符合程序的可观察行为。
  • @Slava 该标准允许实现添加成员函数的重载,只要它们不干扰指定为有效的调用。参见 [global.functions] 和 [member.functions],特别是后者的脚注。
  • @M.M 这是一个比简单地不破坏一致性代码更强大的异常。如果没有这个例外,符合代码可以使用 SFINAE 来检测某个调用是否无效。由于这个异常,调用仍然是有效的。
【解决方案2】:

那么在int8_t 和其他人无法通过这种重载处理的平台上,C++ 流必须如何处理它们?

就整数插入而言(运算符仍然是整数类型,并遵循隐式转换规则以实现重载解析。

例如,int8_t 可以向上转换为任何大到足以容纳它的有符号整数类型。因此,如果int8_t 是扩展整数类型而不是标准整数类型的别名,则可以根据重载解析规则将其上转换为标准整数类型之一。

对于整数提取 (operator&gt;&gt;),这需要一个非const 对活动整数的引用,因此无法进行转换。因此,如果您不提供 operator&gt;&gt; 为其重载的显式类型之一,您的代码应该无法编译。

如果实现专门为这些类型添加重载,您可能会得到最好的结果,但您不能依赖它。

需要注意的是,标准明确规定 C++17 的新 to_charsfrom_chars 具有 all 有符号和无符号整数类型(以及 char)的重载。所以这包括扩展的整数类型。

会不会编译失败?

对于插入,这取决于使用的类型。 int8_t 是安全的,因为有比它大的标准整数类型。它的行为可能与您预期的不同,但它会起作用。

相比之下,不能保证int_least64_tintmax_t 不大于long long。并且隐式转换不能从大到小。在这些情况下,您会收到编译错误。

对于提取,如果类型不匹配,则会导致编译失败。

标准库实现者是否必须提供其他未记录的方法?

没有。他们被允许这样做,但这不是你可以依赖的。


int8_t 是一个特殊情况,因为它是如何实现的。

char 兼作字节大小类型和字符类型。因为它是一种字符类型,所以 iostream 操作将处理 char 的使用与处理整数类型的方式不同。使用整数,它将解析/生成整数字符串。对于一个字符,它假定您的意思是阅读一个字符。

int8_t 的有效实现可以是 signed char 的别名,或者只是 char(如果已签名)。因此,将它与流操作一起使用将treat it as a character

int8_t 的有效实现可以是扩展整数类型,该类型与char 不同。因此,根据重载决议规则,该值将被上转换为与标准之一匹配的更大整数类型。因此,它将call one of these

【讨论】:

  • “例如,int8_t 可以向上转换为任何大到足以容纳它的有符号整数类型。” std::istream 不是这种情况
  • @Slava:当然不是。那是因为您的实现将int8_t 实现为signed char 或类似的东西。这是完全合法的,并且会称其为流操作的char 重载。这不是必需的实现;这只是一种可能性。如果int8_t 被实现为扩展整数类型,那么它不会触发该重载;它会触发不同的过载。这也是一个有效的实现。
  • @Slava:换句话说,标准要求您可以将operator&lt;&lt; 应用于int8_t确切地没有指定将发生什么,确切地调用哪个重载。
  • “当你这样做时会发生什么,但没有指定”如果我理解正确,明确列出了成员重载的确切列表,所以这实际上是一个问题,如何实现不违反标准。
  • @Slava:任何一种方式都是有效的!你不明白的到底是什么?将int8_t 设为char 的别名是合法的。使其成为扩展整数类型也是合法的。当在流上调用operator&lt;&lt; 时,这两种可能的实现有两种截然不同的行为。因此,如果您希望代码跨平台,则不能依赖任何特定行为。
猜你喜欢
  • 1970-01-01
  • 2013-06-13
  • 2022-07-10
  • 1970-01-01
  • 2018-06-13
  • 1970-01-01
  • 1970-01-01
  • 2014-06-03
  • 1970-01-01
相关资源
最近更新 更多