【问题标题】:std::istream operator exception reset / not thrownstd::istream 运算符异常重置/未抛出
【发布时间】:2018-07-09 20:03:12
【问题描述】:

我不确定如何使用std::istream::exception 根据标准,如果std::istream::operator>> 无法将输入读取到变量中,则它会抛出异常,例如双倍的。 以下代码与 clang/libc++ 和 gcc/libstdc++ 有不同的行为:

#include <iostream>
#include <cassert>

int main () {
    double foo,bar;
    std::istream& is = std::cin;

    is.exceptions(std::istream::failbit);
    is >> foo; //throws exception as expected with gcc/libstdc++ with input "ASD"
    std::cout << foo;
    is >> bar;
    std::cout << bar;
    assert(is); //failed with clang/libc++ after input "ASD"

    std::cout << foo << " " << bar << std::endl;

}

根据 C++ 标准,is.exceptions(std::istream::failbit); 是否适合让 operator&gt;&gt; 抛出?

【问题讨论】:

  • @πάνταῥεῖ 他们确定他们使用 libc++?使用 Apple LLVM 版本 7.0.2 (clang-700.1.81) 和 libc++ 1101 复制它。
  • 它使用 libc++ 进行复制。使用 gcc/libstdc++ 与 clang/libstdc++ 比较 std::lib 问题几乎总是毫无意义,但人们仍然坚持这样做:-\
  • 这是LWG issue 2349

标签: c++ libstdc++ c++-standard-library libc++


【解决方案1】:

首先是一些背景信息(如果您想进一步阐述,每一个都在其各自的标题下解释):

  • 标准要求istreams 仅在ios_base::badbit 设置为basic_istream::exceptions 时重新抛出
  • libstdc++ 不符合此要求,但 libc++ 符合
  • libc++ 使请求它镜像 libstdc++ 行为的错误无效
  • libc++ 提供ios_base::badbit 与所需的ios_base::iostate 按位或作为解决方法

不幸的是,这种解决方法的副作用是每当 ios_base::badbit 设置为独立于 ios_base::failbit 时也会重新抛出:http://en.cppreference.com/w/cpp/io/ios_base/iostate#The_badbit

如果您希望在设置ios_base::failbit 时发生抛出,并且您需要它在 libc++ 和 libstdc++ 上具有相同的行为,则必须检查 ios_base::badbitistream 上发生的每个输入操作之后。这需要看起来像这样:

if((is.rdstate() & ios_base::failbit) != 0) throw ios_base::failure("basic_ios::clear");

As noted by cpplearner 你甚至不能使用basic_istream::fail,你必须对istreamrdstate 返回进行逐位测试。但老实说,这只会增加一点复杂性。

使这项任务成为一项艰巨任务的是istream 的使用程度。 istream 的广泛使用可以通过辅助函数来解决,但是使用 istream_iterators 或提取运算符的复合重载很快就会使手动检查这个任务变得不合理。

如果你发现自己在那里,我会认真考虑is.exceptions(ios_base::failbit | ios_base::badbit) 解决方法的可能性。


标准要求istreams 仅在ios_base::badbit 设置为basic_istream::exceptions 时重新抛出

调用basic_istream::exceptions(istream::failbit) 将设置一个掩码,该掩码可以通过调用basic_istream::exceptions() 来检索,根据标准的27.5.5.4 [iosstate.flags]/11 是:

确定rdstate() 中设置的哪些元素会引发异常的掩码。

27.7.2.2.3 [istream::extractors]/15 支持无格式插入方法:

如果由于在*this (27.5.5.4) 中从*thisfailbit 中提取字符时捕获到抛出的异常而未插入任何字符,则重新抛出捕获的异常。

但是,对于格式化输入,这将在 27.7.2.2.1 [istream.formatted.reqmts]/1 中回退;仅当掩码的位与和ios_base::badbit 不为零时才需要抛出:

如果在输入过程中抛出异常,则 ios::badbit*this 的错误状态下打开。如果(exceptions()&amp;badbit) != 0 则重新抛出异常。


libstdc++ 不符合此要求,但 libc++ 符合

ios_base::failbit 应设置在其各自的 istream 上,例如:

basic_istream::operator&gt;&gt; 的数字、指针和布尔输入重载(从技术上讲,他们调用 num_get::get 的重载),如果输入无法解析为有效值或解析的值不适合目标输入。

[Source]

如果在basic_istream::exceptions'掩码上只设置了ios_base::failbit,并且发生了一个事件,导致ios_base::failbit被设置,例如如上所述提取无效数字:


libc++ 使请求它镜像 libstdc++ 行为的错误无效

这个问题有一个now invalidated bug against libc++。引用 27.7.2.1 [istream]/4

如果这些被调用函数之一引发异常,则除非另有明确说明,否则输入函数会将badbit 设置为错误状态。如果badbitexceptions() 中打开,则输入函数会在未完成其操作的情况下重新抛出异常,否则它不会抛出任何东西并继续进行,就好像被调用的函数返回了失败指示一样。


libc++ 提供 ios_base::badbit 按位或所需的 ios_base::iostate 作为解决方法

我们自己的Howard Hinnant(他也恰好是使链接的 libc++ 错误无效的 libc++ 代表)suggests in answer to a duplicate of this question(以及在 libc++ 错误中),您使用了解决方法:

is.exceptions(ios_base::failbit | ios_base::badbit);

【讨论】:

  • 尝试使用-stdlib=libc++,否则你只是在使用与GCC相同的std::lib,这并不能证实clang的库。
  • @JonathanWakely 谢谢,这是一个非常有用的评论。我错误地认为 Clang 默认使用 libc++ 编译。
  • 如果设置了 failbitbadbitfail() 返回 true。只测试failbit,需要写is.rdstate() | std::ios_base::failbit
  • @cpplearner 感谢您的提示。我已经更新了我的答案。看来fail 对这个函数有点用词不当:S 对于它可能关心的人,你的按位或应该是按位的,并且:is.rdstate() &amp; istream::failbit != 0
  • “然而,我已经提供了使用 libc++ 记录此问题的错误报告”...根据他对标准文本的阅读,他的(当时)维护者认为不是错误。也许 Howard Hinnant 是错的,但要证明这一点,需要的不仅仅是来自非权威来源(如 cppreference 或 cplusplus.com)的孤立引用。最新的工作草案 (N4567) 可在线免费获取。
【解决方案2】:

libc++ 似乎遵循了这个要求:

每个格式化的输入函数通过使用noskipws 构造类sentry 的对象开始执行 (第二个)参数false。如果哨兵对象返回true,当转换为bool类型的值时, 函数努力获取请求的输入。 如果在输入过程中抛出异常则ios::badbit*this 的错误状态下开启312。如果 (exceptions()&amp;badbit) != 0 则重新抛出异常。 在任何情况下,格式化的输入函数都会破坏哨兵对象。如果没有抛出异常,则 返回*this

312) 这样做不会导致ios::failure 被抛出。

(引自 N4567 § 27.7.2.2.1 [istream.formatted.reqmts] 但 C++ IS 包含相同的措辞。强调我的)

不过,我不知道这是否真的意味着“如果(exceptions()&amp;badbit) == 0 则输入函数不应抛出任何异常”。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2015-06-03
  • 1970-01-01
  • 2021-12-16
  • 2021-08-07
  • 1970-01-01
  • 2019-10-19
  • 2016-01-08
相关资源
最近更新 更多