应该通过std :: cin(gcc,clang disagree)读取否定为无符号失败?

时间:2018-04-19 12:34:25

标签: c++ language-lawyer c++17 cin c++-standard-library

例如,

#include <iostream>

int main() {
  unsigned n{};
  std::cin >> n;
  std::cout << n << ' ' << (bool)std::cin << std::endl;
}

输入-1时,clang 6.0.0输出0 0,而gcc 7.2.0输出4294967295 1。我想知道谁是对的。或者两者都是正确的标准没有指定这个?如果失败,我认为(bool)std::cin被评估为假。 clang 6.0.0也输入-0失败。

3 个答案:

答案 0 :(得分:18)

我认为在C ++ 17 1 中两者都是错误的,并且预期的输出应该是:

4294967295 0

然而,这很难从标准中掌握......

标准说 - [facet.num.get.virtuals#3.3]

  

第2阶段(字段)中累积的字符序列将通过标题<cstdlib>中声明的其中一个函数的规则转换为数值:

     
      
  • 对于有符号整数值,函数strtoll

  •   
  • 对于无符号整数值,函数strtoull

  •   
  • 对于浮点值,函数strtold

  •   

所以我们回到std::strtoull,在这种情况下必须返回 2 ULLONG_MAX而不设置errno(两个编译器都这样做)。

但是在同一个区块(强调是我的):

  

要存储的数值可以是以下之一:

     
      
  • 零,如果转换函数未转换整个字段。

  •   
  • 最正(或负)可表示的值,如果要转换的字段为有符号整数类型,则表示要在{{中表示的太大正(或负)的值1}}。

  •   
  • 最正可表示的值,如果要转换的字段为无符号整数类型,则表示无法在val中表示的值。

  •   
  • 转换后的值,否则。

  •   
     

结果数值存储在val中。如果转换函数未转换整个字段,或者该字段表示超出可表示值范围的值,则会将val分配给ios_­base​::​failbit

请注意,所有这些都涉及要转换的 “字段” ,而不是err返回的实际值。这里的字段实际上是加宽的字符序列std::strtoull

由于该字段表示无法由'-', '1'表示的值(-1),因此返回的值应为unsigned,并且应在UINT_MAX上设置failbit。

1 std::cin实际上是在C ++ 17之前,因为上面引用的第三个子弹是:

  

- 无符号整数类型的最负值可表示值或零,如果该字段表示的值太大而无法在clang中表示。 val被分配到ios_base::failbit

2 std::strtoull返回err因为(感谢@NathanOliver) - C / 7.22.1.4.5:

  

如果主题序列具有预期形式且base的值为零,则根据6.4.4.1的规则将以第一个数字开头的字符序列解释为整数常量。   [...]   如果主题序列以减号开头,则转换产生的值将被取消(在返回类型中)。

答案 1 :(得分:2)

问题在于库实现之间的差异-与编译器之间的差异()无关。

cppreference很好地清除了这些不一致之处:

  

将负数字符串转换为无符号的结果   指定整数以产生直到,尽管有些   实现遵循std::strtoull的协议,该协议否定了   在目标类型中,将ULLONG_MAX赋予“ -1”,从而产生   而是目标类型的最大值。从开始,严格   遵循std::strtoull是正确的行为。

这总结为:

  • ULLONG_MAX4294967295)是正确的,因为(两个编译器现在都可以纠正)
  • 以前,0应该严格阅读标准(
  • 某些实现(尤其是)遵循std::strtoull协议(现在被认为是正确的行为)

故障位设置及其设置原因可能是一个更有趣的问题(至少从角度而言)。在)第7版中,它现在与相同-这似乎表明它的选择与向前相同(即使这与标准上,在之前应该为零-但到目前为止,我还找不到该更改的更改日志或文档。

读取有趣的文本块(假设在c ++ 17之前):

  

如果转换函数导致负值太大而无法   适合v的类型,则存储了最负的可表示值   在v中,对于无符号整数类型,或为零

因此,该值指定为0。此外,没有任何地方表明这会导致设置故障位。

答案 2 :(得分:0)

std::cin >> n命令的预期语义描述为here(显然,此操作调用了std::num_get::get())。这个函数有一些语义上的变化,特别是w.r.t.选择是否放置0,在C ++ 11中,然后再在C ++ 17中。

我不完全确定,但我相信这些差异可能会影响您所看到的不同行为。