Semântica de bandeiras em basic_ios

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