Подтвердить что ты не робот

Семантика флагов на basic_ios

Я нахожу себя неоднократно озадаченным флагами rdstate() - good(), bad(), eof(), fail() - и как они выражаются в basic_ios::operator!, operator bool и operator void*.

Может ли кто-нибудь вывести меня из моих страданий и объяснить это, чтобы мне больше не приходилось повторять дважды?

4b9b3361

Ответ 1

Существуют три флага, которые указывают состояние ошибки:

  • badbit означает, что что-то пошло не так с потоком. Это может быть ошибка буфера или ошибка в том, что подает данные в поток. Если этот флаг установлен, вероятно, вы больше не будете использовать поток.

  • failbit означает, что извлечение или чтение из потока завершилось неудачно (или запись или вставка для выходных потоков), и вам нужно знать об этом сбое.

  • eofbit означает, что входной поток достиг своего конца, и нет ничего, что можно было бы прочитать. Обратите внимание, что это устанавливается только после того, как вы попытаетесь прочитать из потока ввода, который достиг своего конца (то есть он установлен, когда возникает ошибка, потому что вы пытаетесь прочитать данные, которых там нет).

failbit также может быть задан многими операциями, которые достигают EOF. Например, если в потоке осталось только пробел, и вы попытаетесь прочитать int, вы оба достигнете EOF, и вы не сможете прочитать int, поэтому оба флажка будут установлены.

Функция fail() проверяет badbit || failbit.

Функция good() проверяет !(badbit || failbit || eofbit). То есть, поток хорош, когда ни один из битов не установлен.

Вы можете reset использовать флаги с помощью функции члена ios::clear(); это позволяет вам установить любой из флагов ошибки; по умолчанию (без аргумента), он очищает все три флага.

Потоки не перегружают operator bool(); operator void*() используется для реализации некорректной версии безопасной идиомы bool. Эта перегрузка оператора возвращает значение null, если установлено значение badbit или failbit, а в противном случае - ненулевое значение. Вы можете использовать это для поддержки идиомы тестирования успеха извлечения в качестве условия цикла или другого оператора потока управления:

if (std::cin >> x) {
    // extraction succeeded
}
else {
    // extraction failed
}

Перегрузка operator!() противоположна operator void*(); он возвращает true, если установлены badbit или failbit и false в противном случае. Перегрузка operator!() больше не нужна; это датируется до того, как перегрузки операторов были полностью и последовательно поддержаны (см. вопрос sbi "Почему std:: basic_ios перегружает оператор унарного логического отрицания?" .

С++ 0x исправляет проблему, которая заставляет нас использовать безопасную идиому bool, поэтому в С++ 0x шаблон базового класса basic_ios перегружает operator bool() в качестве явного оператора преобразования; этот оператор имеет ту же семантику, что и текущий operator void*().

Ответ 2

В дополнение к ответ Джеймса важно помнить, что эти флаги указывают на результаты операций, поэтому не будут установлены, если вы не выполните их.

Общей ошибкой является следующее:

#include <fstream>
#include <iostream>
#include <string>

int main()
{
    std::ifstream file("main.cpp");

    while (!file.eof()) // while the file isn't at eof...
    {
        std::string line;
        std::getline(file, line); // ...read a line...

        std::cout << "> " << line << std::endl; // and print it
    }
}

Проблема здесь в том, что eof() не будет установлен до тех пор, пока мы не попытаемся получить последнюю строку, и в этот момент поток скажет "нет, больше!". и установите его. Это означает, что "правильный" способ:

#include <fstream>
#include <iostream>
#include <string>

int main()
{
    std::ifstream file("main.cpp");

    for (;;)
    {
        std::string line;
        std::getline(file, line); // read a line...

        if (file.eof()) // ...and check if it we were at eof
            break;

        std::cout << "> " << line << std::endl;
    }
}

Это помещает проверку в правильное место. Это очень неуправляемо; к счастью для нас, возвращаемое значение для std::getline является потоком, а поток имеет оператор преобразования, который позволяет тестировать его в булевом контексте со значением fail(), который включает в себя eof(). Поэтому мы можем просто написать:

#include <fstream>
#include <iostream>
#include <string>

int main()
{
    std::ifstream file("main.cpp");

    std::string line;
    while (std::getline(file, line)) // get line, test if it was eof
        std::cout << "> " << line << std::endl;
}