Noexcept Function Calling a Function That Can Throw.

Is is good practice to declare a function as noexcept even if it calls a function that can throw?
Example:
1
2
3
4
5
6
7
8
9
10
11
12
Resource loadResource(const std::string &path) const
{
     //...
     if (!file.is_open()) throw std::runtime_error("Could not open file.");
     //...
}

void doSomething() noexcept
{
     Resource importantResource{loadResource("...")}; // Terminates if file is missing.
     //...
}
Last edited on
From https://en.cppreference.com/w/cpp/language/noexcept_spec:
Non-throwing functions are permitted to call potentially-throwing functions. Whenever an exception is thrown and the search for a handler encounters the outermost block of a non-throwing function, the function std::terminate is called
> Is is good practice to declare a function as noexcept even if it calls a function that can throw?

Yes, if
a. we can guarantee that the called function would never throw in practice
or b. if the called function throws, we consider it to be a catastrophic error and it is best to terminate the program.
Is is good practice to declare a function as noexcept even if it calls a function that can throw

The answer is that it depends on the function.

I'll talk about the Committee's general policy for the standard library:

Were you proposing doSomething for use in the standard library, then you would submit a potentially-throwing function unless your function is specified as having a wide contract and isn't specified to throw.

WG21's internal guidelines suggest that functions with a narrow contract should generally not be marked noexcept. Briefly, the rationale is that marking such functions non-throwing precludes testing for its invariants in a systematic way.

A function may acquire a narrow contract as a result of being marked noexcept. For example, the function f has a wide contract:
1
2
3
// N.B.: std::istream::operator>>() is specified to throw
// depending on the stream's exception mask
void f(int &x) { std::cin >> x; }
But the function g (identical to f, but noexcept) has a narrow contract, and should be potentially-throwing:
 
void g(int &x) noexcept { std::cin >> x; }

The policy is at https://wg21.link/p0884r0 , but another paper contains its rationale: https://wg21.link/n3248

I'm not necessarily suggesting that you follow that policy. However, it does explain why many functions in the standard library are potentially-throwing despite not being specified to throw exceptions, and why the library does not supply conditionally-noexcept functions where we might otherwise expect.
Last edited on
Topic archived. No new replies allowed.