例如,
#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
失败。
答案 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)
问题在于库实现libc++和libstdc++之间的差异-与编译器之间的差异(clang,gcc)无关。
cppreference很好地清除了这些不一致之处:
将负数字符串转换为无符号的结果 指定整数以产生零直到c++17,尽管有些 实现遵循
std::strtoull
的协议,该协议否定了 在目标类型中,将ULLONG_MAX
赋予“ -1”,从而产生 而是目标类型的最大值。从c++17开始,严格 遵循std::strtoull
是正确的行为。
这总结为:
ULLONG_MAX
(4294967295
)是正确的,因为c++17(两个编译器现在都可以纠正)0
应该严格阅读标准(libc++)std::strtoull
协议(现在被认为是正确的行为)故障位设置及其设置原因可能是一个更有趣的问题(至少从language-lawyer角度而言)。在libc++(clang)第7版中,它现在与libstdc++相同-这似乎表明它的选择与向前相同(即使这与标准上,在c++17之前应该为零-但到目前为止,我还找不到该更改的更改日志或文档。
读取有趣的文本块(假设在c ++ 17之前):
如果转换函数导致负值太大而无法 适合v的类型,则存储了最负的可表示值 在v中,对于无符号整数类型,或为零。
因此,该值指定为0
。此外,没有任何地方表明这会导致设置故障位。
答案 2 :(得分:0)
std::cin >> n
命令的预期语义描述为here(显然,此操作调用了std::num_get::get()
)。这个函数有一些语义上的变化,特别是w.r.t.选择是否放置0,在C ++ 11中,然后再在C ++ 17中。
我不完全确定,但我相信这些差异可能会影响您所看到的不同行为。