This is an example extracted from section 10.3.3 Input of User-defined Types from the book "The C++ Programming Language" second edition, by B. Stroustrup. The code is old but still compiles with minor changes. Example:
#include <istream>
#include <complex>
using namespace std;
istream& operator>>(istream& s, complex<double>& a)
{
// input formats for a complex; "f" indicates a float:
//
// f
// (f)
// (f, f)
double re = 0, im = 0;
char c = 0;
s >> c;
if( c == '(' ) {
s >> re >> c;
if( c == ',' ) s >> im >> c;
if( c != ')' ) s.clear(ios::badbit); // set state
}
else {
s.putback(c);
s >> re;
}
if( s ) a = complex<double>(re, im);
return s;
}
Despite the scarcity of error-handling code, this will actually handle
most kinds of errors. The local variable c
is initilized to avoid
having its value accidentally '('
after a failed operation. The
final check of the stream state ensures that the value of the argument
a
is changed if everything went well.
I failed to understand the phrase emphasized above.
If s >> c
fails then c
is not written to.
If c
was uninitialized , it remains uninitialized at the point of the test if( c == '(' )
. Reading an uninitialized char
causes undefined behaviour.
The author is talking about a possible way that this undefined behaviour might manifest itself.
The suggested fix of char c = 0;
relies on the fact thats.putback(c);
does nothing if s
is not good()
. This is OK, although IMHO it would be clearer to write:
char c;
s >> c;
if ( !s )
return s;
Then anybody reading the code can immediately see that it behaves properly in case of error; instead of having to thread their way through the flow of the function and check that no other operations will do anything unexpected.
Consider what happens if we didn't initialize c
:
char c;
s >> c;
if (c == '(') { ... }
We are not checking to see if >>
succeeded or failed. So if c == '('
, it could be for one of two reasons:
- The
>>
succeeded and we retrieved the character '('
from the istream
.
- The
>>
failed, but the random byte that happened to be in the memory for c
when it was created is a '('
.
If you initialize c
to 0
, case 2 is impossible: if the operation failed, we know that c
will be 0
... so we will know for sure that if we got a '('
it's because it came from the istream
.