• No se han encontrado resultados

Another problem that we have had to solve in several contexts is reading homework grades into a vector.

There is a problem in designing the behavior of such a function: It needs to return two values at once. One value is, of course, the homework grades that it read. The other is an indication of whether the attempted input was successful. There is no direct way to return more than one value from a function. One indirect way to do so is to give the function a parameter that is a reference to an object in which it is to place one of its results. This strategy is common for

functions that read input, so we'll use it. Doing so will make our function look like this:

// read homework grades from an input stream into a vector<double> istream& read_hw(istream& in, vector<double>& hw) {

// we must fill in this part return in;

}

In §4.1.2/54, we saw a program with a const vector<double>& parameter; now we're

dropping the const. A reference parameter without a const usually signals an intent to modify the object that is the function's argument. For example, when we execute

vector<double> homework; read_hw(cin, homework);

the fact that read_hw's second parameter is a reference should lead us to expect that calling read_hw will change the value of homework.

Because we expect the function to modify its argument, we cannot call the function with just any expression. Instead, we must pass an lvalue argument to a reference parameter.

An lvalue is a value that denotes a nontemporary object. For example, a variable is an lvalue, as is a reference, or the result of calling a function that returns a reference. An expression that generates an arithmetic value, such as sum / count, is not an lvalue. Both of the parameters to read_hw are references, because we expect the function to change the state of both arguments. We don't know the details of how cin works, but presumably the library defines it as a data structure that stores everything the library needs to know about the state of our input file. Reading input from the standard input file changes the state of the file, so it should logically change the value of cin as well.

Notice that read_hw returns in. Moreover, it does so as a reference. In effect, we are saying that we were given an object, which we are not going to copy, and we will return that same object, again without copying it. Returning the stream allows our caller to write

if (read_hw(cin, homework)){/*...*/}

as an abbreviation for read_hw(cin, homework); if (cin) {/*...*/}

We can now think about how to read the homework grades. Obviously, we want to read as many grades as exist, so it would seem as if we could just write

while (in >> x) hw.push_back(x);

This strategy doesn't quite work, for two reasons. The first reason is that we haven't defined hw—our caller defined it for us. Because we didn't define it, we don't know what data might be there already. For all we know, our caller might be using our function to process homework for lots of students, in which case hw might contain the previous student's grades. We can solve this problem by calling hw.clear() before we begin our work.

The second reason that our strategy fails is that we don't quite know when to stop. We can keep reading grades until we can no longer do so, but at that point we have a

problem. There are two reasons why we might no longer be able to read a grade: We might have reached end-of-file, or we might have encountered something that is not a grade. In the first case, our caller will think that we have reached end-of-file. This thought will be true but misleading, because the end-of-file indication will have occurred only after we have successfully read all the data. Normally, an end-of-file indication means that an input attempt failed.

In the second case, when we have encountered something that isn't a grade, the library will mark the input stream as being in failure state, which means that future input

requests will fail, just as if we had reached end-of file. Therefore, our caller will think that something is wrong with the input data, when the only problem was that the last homework grade was followed by something that was not a homework grade.

In either case, then, we would like to pretend that we never saw whatever followed the last homework grade. Such pretense turns out to be easy: If we reached end-of-file, there was no additional input to read; if we encountered something that wasn't a grade, the library will have left it unread for the next input attempt. Therefore, all we must do is tell the library to disregard whatever condition caused the input attempt to fail, be it end-of-file or invalid input. We do so by calling in.clear() to reset the error state inside in, which tells the library that input can continue despite the failure. There is one more detail to consider: Perhaps we have already run out of input, or encountered an error condition, before even trying to read the first homework grade. In that case, we must leave the input stream strictly alone, lest we inadvertently seduce our caller into trying to read nonexistent input at some point in the future.

Here is the complete read_hw function:

// read homework grades from an input stream into a vector<double> istream& read_hw(istream& in, vector<double>& hw)

{

if (in) {

// get rid of previous contents hw.clear() ;

// read homework grades double x;

while (in >> x) hw.push_back(x);

// clear the stream so that input will work for the next student in.clear();

}

return in; }

Note that the clear member behaves completely differently for istream objects than it does for vector objects. For istream objects, it resets any error indications so that input can continue; for vector objects, it discards any contents that the vector might

have had, leaving us with an empty vector again.