std::istream 运算符异常重置/未抛出

std::istream operator exception reset / not thrown

提问人:Superlokkus 提问时间:1/27/2016 最后编辑:Superlokkus 更新时间:1/31/2018 访问量:1111

问:

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

#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>>

C libstdc C++-标准库 libc++

评论

2赞 Superlokkus 1/27/2016
@πάνταῥεῖ 确定他们使用 libc++?使用 Apple LLVM 版本 7.0.2 (clang-700.1.81) 和 libc++ 1101 复制了它。
1赞 Jonathan Wakely 1/27/2016
它使用 libc++ 进行复制。使用 gcc/libstdc++ 和 clang/libstdc++ 比较 std::lib 问题几乎总是毫无意义的,但人们仍然坚持这样做:-\
1赞 Jonathan Mee 1/27/2016
libc++ std::istringstream 的可能副本不会引发异常。错误?
0赞 T.C. 1/28/2016
这是 LWG 问题 2349

答:

7赞 Jonathan Mee 1/27/2016 #1

首先是一些背景信息(如果您想进一步详细说明,下面将在各自的标题下解释这些信息):

  • 该标准要求仅在istreamios_base::badbitbasic_istream::exceptions
  • libstdc++ 不符合此要求,但 libc++ 符合
  • libc++ 使请求镜像 libstdc++ 行为的 bug 无效
  • libc++ 提供按位或所需的解决方法ios_base::badbitios_base::iostate

不幸的是,这种解决方法的副作用是,只要设置独立于:http://en.cppreference.com/w/cpp/io/ios_base/iostate#The_badbitios_base::badbitios_base::failbit

如果您希望仅在设置时发生抛出,并且您需要它在 libc++ 和 libstdc++ 上具有相同的行为,则必须在 .这需要看起来像这样:ios_base::failbitios_base::badbitistream

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

正如 cpplearner 所指出的,您甚至不能使用 basic_istream::fail,您必须对 的 rdstate 返回进行按位测试。但老实说,这只会增加一点复杂性。istream

可能使这成为一项艰巨任务的是它的使用程度。可以通过辅助功能来对抗 的广泛使用,但使用提取操作员的 s 或复合过载会很快使手动检查成为一项不合理的任务。istreamistreamistream_iterator

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


该标准要求仅在istreamios_base::badbitbasic_istream::exceptions

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

一个掩码,用于确定在其中设置的元素会导致引发异常。rdstate()

27.7.2.2.3 [istream::extractors]/15 中支持未格式化的插入方法:

如果它没有插入任何字符,因为它捕获了在从 (27.5.5.4) 中提取字符时引发的异常,并且处于打开状态,则会重新引发捕获的异常。*thisfailbitexceptions()

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

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


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

应该在事件上设置它各自,例如:ios_base::failbitistream

如果输入无法分析为有效值,或者解析的值不适合目标类型,则数值、指针和布尔输入重载(从技术上讲,它们调用的重载)。basic_istream::operator>>num_get::get

[]

如果仅在 ' 掩码上设置了 ,并且发生了导致 设置的事件,例如提取无效数字,如上所述:ios_base::failbitbasic_istream::exceptionsios_base::failbit


libc++ 使请求镜像 libstdc++ 行为的 bug 无效

对于这个问题,现在有一个针对 libc++ 的无效错误。引用 27.7.2.1 [istream]/4

如果其中一个被调用的函数引发异常,则除非另有明确说明,否则输入函数将处于错误状态。如果 在 中 ,则输入函数将重新引发异常而不完成其操作,否则它不会抛出任何内容并继续操作,就好像被调用的函数返回了失败指示一样。badbitbadbitexceptions()


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

我们自己的 Howard Hinnant(他也恰好是 libc++ 的代表,他使链接的 libc++ 错误无效)在回答这个问题的副本(以及 libc++ 错误)时建议您使用解决方法:

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

评论

1赞 Jonathan Wakely 1/27/2016
尝试使用,否则您只是使用与 GCC 相同的 std::lib,这并不能确认有关 clang 库的任何信息。-stdlib=libc++
0赞 Jonathan Mee 1/27/2016
@JonathanWakely 谢谢,这是一个非常有用的评论。我错误地认为 Clang 默认使用 libc++ 编译。
1赞 cpplearner 1/27/2016
fail()如果设置了 OR 则返回 TRUE。仅进行测试,需要编写 .failbitbadbitfailbitis.rdstate() | std::ios_base::failbit
0赞 Jonathan Mee 1/27/2016
@cpplearner 谢谢你的提示。我已经更新了我的答案。对于这个函数来说,这似乎有点用词不当:S 对于它可能关心的人,您的按位或应该是按位的,并且:failis.rdstate() & istream::failbit != 0
1赞 T.C. 1/28/2016
“但是,我已经提供了错误报告,记录了libc ++的这个问题”......其(当时的)维护者根据他对标准文本的阅读,不认为这是一个错误。也许霍华德·辛南特(Howard Hinnant)错了,但要证明这一点,需要的不仅仅是来自cpreference或 cplusplus.com 等非权威来源的孤立引用。最新的工作草案(N4567)可在线免费获取。
2赞 cpplearner 1/27/2016 #2

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

每个格式化的输入函数都通过使用 (second) 参数构造一个 class 对象来开始执行。如果 sentry 对象返回 ,当转换为 type 的值时, 函数努力获取请求的输入。如果在输入过程中抛出异常,则 ios::badbit*this 的错误状态下打开312。如果 (exceptions()&badbit) != 0,则重新抛出异常。在任何情况下,格式化的输入函数都会销毁哨兵对象。如果没有引发异常,则 返回。sentrynoskipwsfalsetruebool*this

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

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

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

评论

0赞 πάντα ῥεῖ 1/27/2016
这听起来终于听起来很有道理了。
0赞 Jonathan Mee 1/27/2016
有趣的是,我链接为重复项,这是霍华德·辛南特(Howard Hinnant)用来为这个错误辩护无效的确切段落。但我不相信这个错误是无效的。我相信我的答案中的解决方案:可能是使用 libstdc++ 纠正 libc++ 不当行为的唯一方法。if(is.fail()) throw ios_base::failure("basic_ios::clear");