Стойността се съхранява, когато четенето на istream е неуспешно

Примерен код:

#include <iostream>

int main()
{
    int x = 5;
    std::cin >> x;
    std::cout << x << '\n';
}

При една конкретна реализация възниква следното поведение:

  • Вход: 6; изход 6
  • Вход: a; изход: 0
  • Вход: (край на файла); изход 5
  • Вход: (празно пространство, последвано от край на файла); изход 5

Така че, при повреда, cin >> x присвоява 0 на x, ако преобразуването на текст в int е било погрешно; но не присвоява 0, ако грешката се дължи на край на файла.

Това правилно поведение ли е? Ако не, какво е правилното поведение според стандарта C++?

Имам спомен от предишно обсъждане на SO, че всички случаи трябва да пишат 0 от C++11, но не можах да намеря нищо с помощта на функцията за търсене; и секцията iostreams на стандарта C++ е доста тежка.


person M.M    schedule 05.10.2015    source източник


Отговори (2)


Съгласно 27.7.2.2.1 [istream.formatted.reqmts] параграф 1, първите неща за функцията за форматиран вход е да се конструира std::istream::senty обект. По-нататъшната обработка зависи от това дали този обект се преобразува в true или false: нищо не се случва със стойността, ако sentry се преобразува в false.

Съгласно 27.7.2.1.3 [istream::sentry] параграфи 5 и 7 sentry ще се преобразува в false, ако флаговете на потока не са std::ios_base::goodbit. Тоест, ако възникне повреда или се достигне EOF, sentry ще се преобразува в false. В резултат на това value остава на 5, когато се достигне EOF след пропускане на интервали, ако приемем, че std::ios_base::skipws е зададено. Отмяната на std::ios_base::skipws трябва да доведе до това, че стойността става 0, ако има поне един интервал.

След като анализирането действително е извършено, логиката на прилагане се дефинира в 22.4.2.1.2 [facet.num.get.virtuals] параграф 3, етап 3. Ключовият раздел за засегнатата стойност е

...

Числовата стойност, която трябва да бъде съхранена, може да бъде една от:

— нула, ако функцията за преобразуване не успее да преобразува цялото поле. ios_base::failbit е присвоен на err.

— най-положителната представима стойност, ако полето представлява твърде голяма положителна стойност, за да бъде представена в val. ios_base::failbit е присвоен на err.

— най-отрицателната представима стойност или нула за тип цяло число без знак, ако полето представлява твърде голяма отрицателна стойност, за да бъде представена в val. ios_base::failbit е присвоен на err.

— преобразуваната стойност, в противен случай.

Получената числова стойност се съхранява в val.

Така че наблюдаваното поведение е правилно.

С преди C++11 стойността беше оставена непроменена във всички случаи. Счита се за желателно да се разграничат грешките и да се посочи със стойността коя стойност трябва да бъде представена. Дискусиите за това как да се промени поведението продължиха доста дълго време и всъщност бяха доста спорни.

Фактът, че стойността не се променя, ако EOF бъде достигнат преди опит за преобразуване, може да се счита за грешка. Не си спомням този случай да е разглеждан, докато се обсъждаше промяната.

person Dietmar Kühl    schedule 05.10.2015

Да, това е правилното поведение след C++11.

Разликата в това, което виждате е, че се пише нула „когато извличането е неуспешно“, но извличането дори не се опитва, ако EOF вече е зададен на потока, така че нищо не се случва.

person Lightness Races in Orbit    schedule 05.10.2015