Семантика флагов на basic_ios - PullRequest
27 голосов
/ 23 ноября 2010

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

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

Ответы [ 2 ]

22 голосов
/ 23 ноября 2010

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

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

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

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

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

Функциональные тесты fail() badbit || failbit.

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

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

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

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

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

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

16 голосов
/ 23 ноября 2010

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

Распространенная ошибка заключается в следующем:

#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;
}
...