提问人:Superlokkus 提问时间:1/27/2016 最后编辑:Superlokkus 更新时间:1/31/2018 访问量:1111
std::istream 运算符异常重置/未抛出
std::istream operator exception reset / not thrown
问:
我不确定如何根据标准使用,如果它无法将输入读取到变量中,例如 double,则会抛出异常。
以下代码对 clang/libc++ 和 gcc/libstdc++ 具有不同的行为:std::istream::exception
std::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>>
答:
首先是一些背景信息(如果您想进一步详细说明,下面将在各自的标题下解释这些信息):
- 该标准要求仅在
istream
ios_base::badbit
basic_istream::exceptions
- libstdc++ 不符合此要求,但 libc++ 符合
- libc++ 使请求镜像 libstdc++ 行为的 bug 无效
- libc++ 提供按位或所需的解决方法
ios_base::badbit
ios_base::iostate
不幸的是,这种解决方法的副作用是,只要设置独立于:http://en.cppreference.com/w/cpp/io/ios_base/iostate#The_badbitios_base::badbit
ios_base::failbit
如果您希望仅在设置时发生抛出,并且您需要它在 libc++ 和 libstdc++ 上具有相同的行为,则必须在 .这需要看起来像这样:ios_base::failbit
ios_base::badbit
istream
if((is.rdstate() & ios_base::failbit) != 0) throw ios_base::failure("basic_ios::clear");
正如 cpplearner 所指出的,您甚至不能使用 basic_istream::fail
,您必须对 的 rdstate
返回进行按位测试。但老实说,这只会增加一点复杂性。istream
可能使这成为一项艰巨任务的是它的使用程度。可以通过辅助功能来对抗 的广泛使用,但使用提取操作员的 s 或复合过载会很快使手动检查成为一项不合理的任务。istream
istream
istream_iterator
如果您发现自己在那里,我会认真考虑解决方法的可能性。is.exceptions(ios_base::failbit | ios_base::badbit)
该标准要求仅在istream
ios_base::badbit
basic_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) 中提取字符时引发的异常,并且处于打开状态,则会重新引发捕获的异常。
*this
failbit
exceptions()
但是,对于格式化的输入,这在 27.7.2.2.1 [istream.formatted.reqmts]/1 中倒退;要求仅当掩码的按位和 且不为零时才发生抛出:ios_base::badbit
如果在输入过程中抛出异常,则在 错误状态下打开。如果,则重新引发异常。
ios::badbit
*this
(exceptions()&badbit) != 0
libstdc++ 不符合此要求,但 libc++ 符合
应该在事件上设置它各自,例如:ios_base::failbit
istream
如果输入无法分析为有效值,或者解析的值不适合目标类型,则数值、指针和布尔输入重载(从技术上讲,它们调用的重载)。
basic_istream::operator>>
num_get::get
[源]
如果仅在 ' 掩码上设置了 ,并且发生了导致 设置的事件,例如提取无效数字,如上所述:ios_base::failbit
basic_istream::exceptions
ios_base::failbit
libc++ 使请求镜像 libstdc++ 行为的 bug 无效
对于这个问题,现在有一个针对 libc++ 的无效错误。引用 27.7.2.1 [istream]/4
如果其中一个被调用的函数引发异常,则除非另有明确说明,否则输入函数将处于错误状态。如果 在 中 ,则输入函数将重新引发异常而不完成其操作,否则它不会抛出任何内容并继续操作,就好像被调用的函数返回了失败指示一样。
badbit
badbit
exceptions()
libc++ 提供按位或所需的解决方法ios_base::badbit
ios_base::iostate
我们自己的 Howard Hinnant(他也恰好是 libc++ 的代表,他使链接的 libc++ 错误无效)在回答这个问题的副本(以及 libc++ 错误)时建议您使用解决方法:
is.exceptions(ios_base::failbit | ios_base::badbit);
评论
-stdlib=libc++
fail()
如果设置了 OR 则返回 TRUE。仅进行测试,需要编写 .failbit
badbit
failbit
is.rdstate() | std::ios_base::failbit
fail
is.rdstate() & istream::failbit != 0
libc++ 似乎遵循了这个要求:
每个格式化的输入函数都通过使用 (second) 参数构造一个 class 对象来开始执行。如果 sentry 对象返回 ,当转换为 type 的值时, 函数努力获取请求的输入。如果在输入过程中抛出异常,则
ios::badbit
在*this
的错误状态下打开312。如果 (exceptions()&badbit) != 0
,则重新抛出异常。在任何情况下,格式化的输入函数都会销毁哨兵对象。如果没有引发异常,则 返回。sentry
noskipws
false
true
bool
*this
312) 这样做不会导致抛出
ios::failure
。
(引自 N4567 § 27.7.2.2.1 [istream.formatted.reqmts],但 C++ IS 包含相同的措辞。强调我的)
不过,我不知道这是否真的意味着“如果输入函数不会抛出任何异常”。(exceptions()&badbit) == 0
评论
if(is.fail()) throw ios_base::failure("basic_ios::clear");
评论