Por qué iostream::eof dentro de una condición de bucle se considera incorrecto en C++?

No es necesariamente incorrecto. Sólo es incorrecto algo así como el 99,99% de las veces (o así).

Hay un par de problemas con él. Let’s start by considering a distillation of typical code that attempts to use it:

  1. while (!somefile.eof()) { 
  2. file.read(data); 
  3. process(data); 

El primer problema aquí es también el más simple de evitar: sólo comprobar el final del archivo es generalmente inadecuado. Si intenta leer, y falla sin que llegue al final del archivo, entrará en un bucle infinito, intentando leer del archivo, y fallando repetidamente-pero sin hacer nada para solucionar el problema o salir del bucle.

Sin embargo, arreglar esto es bastante fácil-en lugar de comprobar eof(), podría intentar algo como: while (somefile.good()) { … }. Esto es al menos una pequeña mejora – al menos ahora, si la lectura del archivo falla, no entrarás en un bucle infinito.

Sin embargo, todavía tiene otro problema bastante serio. Este problema es con la secuenciación. Comienza comprobando si la lectura desde el archivo ha fallado. Luego lee algunos datos, procesa los datos, y repite.

El problema con eso es simple: lee los datos, luego procesa los datos, y sólo después de haber procesado los datos, intenta comprobar si realmente leyó los datos correctamente. Por si no ha quedado claro, lo repito (porque es importante): intenta leer unos datos, luego, tanto si ha tenido éxito como si no, intenta procesar como si lo hubiera tenido, y sólo después de haber terminado de procesar unos datos que puede que no haya leído realmente, comprueba si ha leído esos datos correctamente.

Espero que eso haga evidente el enfoque correcto: tenemos que cambiar la secuencia: tenemos que intentar leer unos datos, luego, si y sólo si la lectura de los datos ha tenido éxito, podemos procesar esos datos. And continue doing that until reading data fails. When it does fail, then we an sort out whether we reached the end of the file (so we read all the data correctly, and life is good) or it failed for some other reason (so we may have a problem).

To do that, we typically want to change the loop to check the result of the read itself. For example:

  1. while (std::getline(somefile, somestring)) 
  2. process(somestring); 

or:

  1. while (somefile.read(buffer, buffer_size)) 
  2. process(buffer, somefile.gcount()); 

Then after that loop we can check why we exited the loop:

  1. if (somefile.eof()) 
  2. std::cout << “processed all the datan”; 
  3. else if (somefile.fail()) 
  4. std::cerr << “Error reading data from filen”; 
  5. else if (somefile.bad()) 
  6. std::cerr << “Serious, unrecoverable error attempting to read filen”; 

Of these, fail() usually means a conversion has failed—for example, we were trying to read a number, but what we found in the file was “yes”. Por el contrario, bad() normalmente significa que hemos recibido un error del sistema operativo que indica que hubo un problema de lectura del archivo, como por ejemplo: «perdimos la conexión con el servidor donde se encontraba ese archivo», o tal vez: «hubo un error de CRC tratando de leer ese sector, por lo que algunos de los datos deben ser malos», o algo en ese orden.