An exception specification declares which exceptions a function may throw. Older specifications promised clearer documentation and possible performance gains, but they became complicated, misleading, and difficult to maintain. section 2.2 section 10.8
Treat these declarations cautiously:
- Avoid legacy throw(...) specifications in modern C++; they were deprecated in C++11 and removed in C++17. section 2.2
- A specification can hide transitive failures: a function may call another function that throws an exception not listed in its declaration. section 10.8
- Use noexcept for functions deliberately designed not to throw; it preserves the useful nonthrowing optimization without the old exception list. section 2.2
A specification that breaks recovery
A standard-deviation function declares only std::invalid_argument, but calls arith_mean before checking the input size. Empty input makes arith_mean throw std::domain_error, violating the declaration and terminating the program before the caller's catch block can recover. Validate inputs before calling throwing operations, or remove the obsolete specification. section 10.8
Listing 2.4 Functions communicating modern nonthrowing behavior
struct Circle {
double radius;
explicit Circle(double r) : radius(r) {}
double perimeter() const noexcept { return 2 * std::numbers::pi * radius; } #1
double area() const noexcept { return std::numbers::pi * radius * radius; } #1
};
int main() {
Circle c(3);
std::cout << "perimeter " << c.perimeter() << ", area " << c.area() << '\n';
return 0;
}
Compare legacy throw() declarations on Circle methods with modern noexcept declarations for explicit nonthrowing behavior. section 2.2
