从istream读取自定义类型失败时的预期行为

Expected behaviour when failing reading a custom type from an istream

本文关键字:失败 istream 读取 自定义 类型      更新时间:2023-10-16

假设我有一个类有两个数据成员:

class X {
    std::string a;
    int b;
public:
    ostream& print(ostream& os);
    istream& read(istream& is);
}

print函数输出所有格式良好的数据,如下所示:

ostream& X::print(ostream& os) {
    return os << a << ' ' << b;
}

现在,read函数应该做相反的事情:读取指定格式的内容(比如horse 54,然后会产生a = "horse"b = 54)。

假设输入不遵循此格式。我的直觉是,一旦我们遇到一个意想不到的字符(例如,在尝试读取int时的一个字母),我们就会设置failbit将我们读取的每个字符都放回流中。在这种情况下,read函数(或operator>>)的预期行为是什么?标准库的行为如何?如果读取失败,是否应该要求我们撤消所有字符提取?

通常情况下,在失败的情况下,您应该将所涉及的任何对象保留为有效状态。这是否意味着回滚要写入的变量、部分写入变量或其他任何内容,取决于您试图实现的目标以及您认为对用户最有利的目标。在任何情况下,一定要记录您的选择,这样用户就可以进行编程,知道您的实现将如何运行。

正如Anton已经说过的那样,标准库似乎没有做出任何努力将字符重新放入流中。另一个似乎更接近您正在做的事情的实际示例是std::complex类的operator>>,它实际上必须在完成之前读取序列中的多个令牌。

template<typename _Tp, typename _CharT, class _Traits>
  basic_istream<_CharT, _Traits>&
  operator>>(basic_istream<_CharT, _Traits>& __is, complex<_Tp>& __x)
  {
    _Tp __re_x, __im_x;
    _CharT __ch;
    __is >> __ch;
    if (__ch == '(') 
  {
    __is >> __re_x >> __ch;
    if (__ch == ',') 
      {
        __is >> __im_x >> __ch;
        if (__ch == ')') 
      __x = complex<_Tp>(__re_x, __im_x);
        else
      __is.setstate(ios_base::failbit);
      }
    else if (__ch == ')') 
      __x = __re_x;
    else
      __is.setstate(ios_base::failbit);
  }
    else 
  {
    __is.putback(__ch);
    __is >> __re_x;
    __x = __re_x;
  }
    return __is;
  }

标准库在出现错误时从不尝试将字符放回流中。例如,以下是关于如何从流(27.7.2.2.2/2)中读取short的标准摘录:

operator>>(short& val);

转换就像由以下代码片段执行一样(使用与前一代码片段相同的符号):

typedef num_get<charT,istreambuf_iterator<charT,traits> > numget;
iostate err = ios_base::goodbit;
long lval;
use_facet<numget>(loc).get(*this, 0, *this, err, lval);
if (lval < numeric_limits<short>::min()) {
    err |= ios_base::failbit;
    val = numeric_limits<short>::min();
} else if (numeric_limits<short>::max() < lval) {
    err |= ios_base::failbit;
    val = numeric_limits<short>::max();
} else
    val = static_cast<short>(lval);
setstate(err);

您可以看到,即使该数字不适合short,也不会恢复任何内容。