<< hg.v2 << endl;
cout << "Sorry, you don't get to play any more.\n";
break;
} // end of catch block
}
cout << "Bye!\n";
return 0;
}
double hmean(double a, double b)
{
if (a == -b)
throw bad_hmean(a,b);
return 2.0 * a * b / (a + b);
}
double gmean(double a, double b)
{
if (a < 0 || b < 0)
throw bad_gmean(a,b);
return std::sqrt(a * b);
}
Here’s a sample run of the program, one that gets terminated by bad input for the gmean() function:
Enter two numbers: 4 12
Harmonic mean of 4 and 12 is 6
Geometric mean of 4 and 12 is 6.9282
Enter next set of numbers : 5 -5
hmean(5, -5): invalid arguments: a = -b
Try again.
5 -2
Harmonic mean of 5 and -2 is -6.66667
gmean() arguments should be >= 0
Values used: 5, -2
Sorry, you don't get to play any more.
Bye!
One point to notice is that the bad_hmean handler uses a continue statement, whereas the bad_gmean handler uses a break statement. Thus, bad input to hmean() leads the program to skip the rest of the loop and start the next loop cycle. But bad input for gmean() terminates the loop. This illustrates how a program can determine which exception is thrown (by the exception type) and tailor the response to the exception.
A second point to notice is that the bad_gmean design illustrates techniques that are different from what bad_hmean uses. In particular, bad_gmean uses public data and a method that returns a C-style string.
Exception Specifications Meet C++11
Sometimes an idea that seems promising in principle doesn’t work out so well in practice. Such is the story of
However, before you ignore exception specifications, you at least should know what they look like. They look like this:
double harm(double a) throw(bad_thing); // may throw bad_thing exception
double marm(double) throw(); // doesn't throw an exception
The throw() part, with or without a list of types, is the exception specification, and it would appear in both the prototype and the function definition.
One reason for exception specifications was to alert the user to the possible need of a try block. However, that can be accomplished just as easily with a comment. A second reason was to allow the compiler to add code to do runtime checks to see if the exception specification was violated. This can happen fairly easily. For instance, marm() might not throw an exception, but it might call a function that calls a function that throws an exception. And maybe that function didn’t throw an exception when you wrote the code, but after a library update, it now does. Anyway, the consensus that developed in the programming community, particularly among those who worked most diligently in writing exception-safe code, was that this feature is best ignored. And now you, too, can ignore exception specifications with the blessings of C++11.
However, C++11 does allow for one special specification—the new keyword noexcept can be used to indicate a function that does not throw an exception:
double marm() noexcept; // marm() doesn't throw an exception
There is some debate about the necessity and usefulness of this specification, with some feeling it’s better to avoid using it (at least, in most cases). But others felt strongly enough about the need to introduce a new keyword. It’s thought that knowing that a function shouldn’t throw an exception can help the compiler optimize code. This usage should be thought of as a promise made by the programmer of the function.
There also is a noexcept() operator (see Appendix E) that reports on whether or not its operand could throw an exception.
Unwinding the Stack
Suppose a try block doesn’t contain a direct call to a function that throws an exception but that it calls a function that calls a function that throws an exception. Execution still jumps from the function in which the exception is thrown to the function that contains the try block and handlers. Doing so involves