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

时间:2016-01-26 17:11:43

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

我不确定如何根据标准使用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>>扔掉?

2 个答案:

答案 0 :(得分:7)

首先是一些背景信息(如果您想进一步阐述,每个背景信息将在下面的相应标题下解释):

  • istream
  • 中设置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::badbit上发生每个输入操作后{1}}。这需要看起来像这样:

istream

As noted by cpplearner你甚至无法使用basic_istream::fail,你必须对if((is.rdstate() & ios_base::failbit) != 0) throw ios_base::failure("basic_ios::clear"); 的{​​{3}}回复进行逐步测试。但老实说,这只会增加一些复杂性。

使这成为一项重大任务的原因是istream的使用程度。 istream的广泛使用可以通过辅助函数来解决,但是使用istream s或提取运算符的复合重载会使人工检查这一任务变得不合理。

如果您发现自己在那里,我会认真考虑istream_iterator解决方法的可能性。

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

中设置istream时,标准要求ios_base::badbit仅重新提交

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

  

确定basic_istream::exceptions()中设置的元素会导致抛出异常的掩码。

对于未格式化的插入方法,27.7.2.2.3 [istream :: extractors] / 15支持此功能:

  

如果它没有插入任何字符,因为它在rdstate()中提取了*thisfailbit中的字符时捕获了异常,那么捕获的异常就会被重新抛出。

但是对于格式化输入,这将在27.7.2.2.1 [istream.formatted.reqmts] / 1中进行逆行;只有在按位且掩码和exceptions()非零时才要求抛出:

  

如果在输入过程中抛出异常,则会在ios_base::badbit的错误状态中打开ios::badbit。如果*this则重新抛出异常。

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

(exceptions()&badbit) != 0应设置在相应ios_base::failbit的事件上,例如:

  

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

[rdstate]

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

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

这个问题有一个no such exception is thrown。引用27.7.2.1 [istream] / 4

  

如果其中一个被调用的函数抛出异常,则除非另有明确说明,否则输入函数会将ios_base::failbit设置为错误状态。如果badbit中有badbit,则输入函数会重新抛出异常而不完成其操作,否则它不会抛出任何内容并继续进行,就像被调用函数返回了失败指示一样。

libc ++使用所需的exceptions()逐位提供ios_base::badbit作为解决方法

我们自己的now invalidated bug against libc++(也恰好是libc ++的代表,它使链接的libc ++ bug失效)Howard Hinnant(以及libc ++ bug)你使用了解决方法:

ios_base::iostate

答案 1 :(得分:2)

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

  

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

     

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

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

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