Warning C26440

Function can be declared 'noexcept'.

C++ Core Guidelines F.6: If your function may not throw, declare it noexcept

If code isn't supposed to cause any exceptions, it should be marked by using the noexcept specifier. This annotation helps to simplify error handling on the client code side, and enables the compiler to do more optimizations.

Remarks

  • A function is considered non-throwing if:
    • it has no explicit throw statements;
    • function calls in its body, if any, invoke only functions that are unlikely to throw: constexpr or functions marked with any exception specification that entails non-throwing behavior (including some non-standard specifications).
  • Non-standard and outdated specifiers like throw() or __declspec(nothrow) aren't equivalent to noexcept.
  • Explicit specifiers noexcept(false) and noexcept(true) are respected appropriately.
  • Functions marked as constexpr aren't supposed to cause exceptions and aren't analyzed.
  • The rule also applies to lambda expressions.
  • The logic doesn't consider recursive calls as potentially non-throwing. This logic may change in the future.

Example

All functions except the destructor will warn because they're missing noexcept.

struct S
{
    S() {} // C26455, Default constructor may not throw. Declare it 'noexcept'
    ~S() {}

    S(S&& s) {/*impl*/} // C26439, This kind of function may not throw. Declare it 'noexcept' (f.6)
    S& operator=(S&& s) {/*impl*/} // C26439, This kind of function may not throw. Declare it 'noexcept' (f.6)

    S(const S& s) {/*impl*/} // C26440, This function can be declared 'noexcept'
    S& operator=(const S& s) {/*impl*/} // C26440, This function can be declared 'noexcept'
};

With noexcept decorating the same structure, all warnings are removed.

struct S
{
    S() noexcept {}
    ~S() {}

    S(S&& s) noexcept {/*impl*/}
    S& operator=(S&& s) noexcept {/*impl*/}

    S(const S& s) noexcept {/*impl*/}
    S& operator=(const S& s) noexcept {/*impl*/}
};