Should I declare all members/function that doesn't throw noexcept?

Viewed 263

One of the CppCoreGuidelines is E.12: Use noexcept when exiting a function because of a throw is impossible or unacceptable. Does it mean that I should declare noexcept on every member and functions that don't throw exceptions and doesn't call other methods/functions that throw? I know it's a good prevaricate to always declare const as much as possible, and I see a lot of const methods in different projects, but I haven't seen noexcept used that much.

2 Answers

Should I declare all members/function that doesn't throw noexcept?

This answer will not attempt to answer whether this is a good practice or not (opinionated), but note that the practice is common when working with safety-critical C++ development, and stricter C++ Guidelines commonly require that all declarations of non-throwing functions contain the noexcept specifier.

AUTOSAR C++14 Guidelines

As an example, Rule A15-4-4 from the Guidelines for the use of the C++14 language in critical and safety-related systems from AUTOSAR (an inofficial but from the industry de facto successor of MISRA C++:2008) covers this practice along with a rationale:

Rule A15-4-4 (required, implementation, automated)

A declaration of non-throwing function shall contain noexcept specification.

Rationale

Noexcept specification is a method for a programmer to inform the compiler whether or not a function should throw exceptions. The compiler can use this information to enable certain optimizations on non-throwing functions as well as enable the noexcept operator, which can check at compile time if a particular expression is declared to throw any exceptions.

Noexcept specification is also a method to inform other programmers that a function does not throw any exceptions.

A non-throwing function needs to declare noexcept specifier. A function that may or may not throw exceptions depending on a template argument, needs to explicitly specify its behavior using noexcept() specifier.

Note that it is assumed that a function which does not contain explicit noexcept specification throws exceptions, similarly to functions that declare noexcept(false) specifier.

The required tag is applied to rules are required to fulfill if the code base is to be claimed to fulfill compliance to the guideline:

5.1.2 Rule classification according to obligation level

required: these are mandatory requirements placed on the code. C++ code that is claimed to conform to AUTOSAR C++14 shall comply with every “Required” rule. Formal deviations must be raised where this is not the case.

Naturally it is up to the particular company/supplier to decide whether and how to comply with AUTOSAR C++14, but it is common that an OEM require compliance to a particular conding standard, e.g. for C++ MISRA C++:2008 or AUTOSAR C++14, and deviations from particular rules commonly need to go through a formal deviation process.

The automated tag is applied to rules that can easily be automatically enforced by means of static analysis.

You should declare every function which cannot or should not throw by design noexcept.
No more, no less.

Remember that consumers cannot know your intentions unless you spell them out, and they will rely on what you write. That is not to detract from there being hordes of them trying to muddle through without reading anything.

If you codify coincidence, you paint yourself into a corner, and will struggle to improve on the implementation.
If you fail to codify your contract, consumers will have to devine it from the implementation, and they will read more into it than you intended, painting you into a corner (again).

That is not to say that there aren't rules and guidelines out there which mistake coincidence (some property does (not) hold) with intent (some property should / must not hold).

Related