Eu me vejo repetidamente perplexo com os rdstate()
– good()
, bad()
, eof()
, fail()
– e como eles são expressos em basic_ios::operator!
, operator bool
e operator void*
.
Alguém poderia me tirar da minha miséria e explicar isso para que eu nunca tenha que pensar duas vezes de novo?
Existem três sinalizadores que indicam o estado do erro:
badbit
significa que algo deu errado com o stream. Pode ser um erro de buffer ou um erro na alimentação de dados para o stream. Se esse sinalizador estiver definido, é provável que você não use mais o stream.
failbit
significa que uma extração ou uma leitura do stream falhou (ou uma gravação ou inserção para streams de saída) e você precisa estar ciente dessa falha.
eofbit
significa que o stream de input chegou ao fim e não há mais nada para ler. Observe que isso é definido somente depois que você tentar ler de um stream de input que atingiu seu final (ou seja, ele é configurado quando ocorre um erro porque você tenta ler dados que não estão lá).
O failbit
também pode ser definido por muitas operações que atingem o EOF. Por exemplo, se houver apenas espaço em branco restante no stream e você tentar ler um int
, ambos atingirão EOF e você não conseguirá ler o int
, portanto, ambos os sinalizadores serão definidos.
A function fail()
testa o badbit || failbit
badbit || failbit
.
A function good()
testa !(badbit || failbit || eofbit)
. Ou seja, um stream é bom quando nenhum dos bits está definido.
Você pode redefinir os sinalizadores usando a function de membro ios::clear()
; isso permite que você defina qualquer um dos sinalizadores de erro; por padrão (sem argumento), apaga todos os três sinalizadores.
Fluxos não sobrecarregam o operator bool()
; operator void*()
é usado para implementar uma versão um tanto quebrada do idiom do bool seguro. Essa sobrecarga de operador retorna null se badbit
ou failbit
estiver definido e não-nulo caso contrário. Você pode usar isso para suportar o idioma de testar o sucesso de uma extração como a condição de um loop ou outra instrução de stream de controle:
if (std::cin >> x) { // extraction succeeded } else { // extraction failed }
A sobrecarga do operator!()
É o oposto do operator void*()
; ele retorna true
se o badbit
ou o failbit
estiver configurado e false
caso contrário. A sobrecarga do operator!()
Não é mais necessária; ele remonta a antes que as sobrecargas do operador fossem suportadas completa e consistentemente (veja a pergunta do sbi “Por que o std :: basic_ios sobrecarrega o operador unário de negação lógica?” ).
C ++ 0x corrige o problema que nos faz ter que usar o idiom do bool seguro, portanto, em C ++ 0x o modelo de class base basic_ios
sobrecarrega o operator bool()
como um operador de conversão explícito; este operador tem a mesma semântica do operator void*()
atual operator void*()
.
Além da resposta de James , é importante lembrar que esses sinalizadores indicam resultados de operações, portanto, não serão definidos, a menos que você execute um.
Um erro comum é fazer isso:
#include #include #include 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 } }
O problema aqui é que eof()
não será definido até que tentemos obter a última linha, ponto em que o stream dirá "não, não mais!" e configurá-lo. Isso significa que a maneira "correta" é:
#include #include #include 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; } }
Isso coloca o cheque no local correto. Isso é muito indisciplinado; Felizmente para nós, o valor de retorno para std::getline
é o stream, e o stream tem um operador de conversão que permite que ele seja testado em um contexto booleano, com o valor de fail()
, que inclui eof()
. Então podemos apenas escrever:
#include #include #include 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; }