2014-10-02 2 views
3

Скажем, у меня есть класс с двумя членами данных:Ожидаемое поведение при неудачном чтении пользовательского типа из IStream

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>>) в этом случае? Как работает стандартная библиотека? Должна ли нам потребоваться отменить все изъятие символов, если сбой чтения?

+0

Читая документы для [уничтожать в зародыше] (http://en.cppreference.com/w/cpp/io/basic_istream/unget), похоже, будет уничтожать в зародыше конкретно не в состоянии, когда ничего, кроме 'goodbit' установлен (и, возможно, даже [throw] (http://en.cppreference.com/w/cpp/io/basic_ios/exceptions). Поэтому я думаю, что если вам нужно обрабатывать плохое форматирование в 'operator >>', вы обычно хотите используйте исключение. – Jason

ответ

1

В общем случае в случае отказа вы ожидаете оставить объект включенным в действующее состояние. Независимо от того, относится ли это к откату от записываемой переменной, частично записывая ее или что-то еще, зависит от того, что вы пытаетесь выполнить, и того, что, по вашему мнению, будет лучше для пользователя. В любом случае, не забудьте документировать свой выбор, чтобы пользователи могли программировать, зная, как будет вести себя ваша реализация.

Как уже сказал Антон, стандартная библиотека, похоже, не прилагает никаких усилий, чтобы положить персонажей в поток. Другим практическим примером, который, кажется, ближе к тому, что вы делаете, является operator>> для класса std::complex, который фактически должен прочитать несколько токенов в последовательности перед выполнением.

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; 
    } 
1

Стандартная библиотека никогда не пытается вернуть символы в поток в случае ошибки. Например, вот выдержка из стандарта о том, как short считывается из потока (27.7.2.2.2/2):

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, ничего не вернулось.