In what cases does a C++ compiler infer noexcept?

Viewed 486

Suppose a C++ compiler is compiling a function whose definition is available in the same translation unit as its invocation. Suppose that it does not throw itself nor calls a function that is know to throw. Suppose also no extern C code is called, nor a numeric division with a potentially-zero value.

Under these assumptions, will the compiler treat the function as noexcept? If not, are there additional conditions under which noexcept is inferred?

Specifically, what about super-simple functions such as

void foo() { } /* this one */
class A { 
    int x_; 
public: 
    x() const { return x_; }  /* ... and this one */
}

?

I'd like an answer based only on the standard, foremost, and possibly also what GCC and clang do.

1 Answers

Almost all functions are assumed to be potentially throwing unless you explicitly use a noexcept specifier. The exceptions are for your own definitions of delete (deallocation functions), and some special member functions: constructors, destructors, and assignment operators. (C++17)

From [except.spec]

If a declaration of a function does not have a noexcept-specifier, the declaration has a potentially throwing exception specification unless it is a destructor or a deallocation function or is defaulted on its first declaration, in which cases the exception specification is as specified below and no other declaration for that function shall have a noexcept-specifier.

Constructors

Are implicitly noexcept unless any initialization performed for any member (or a member's member etc) is potentially throwing

Destructors

Are implicitly noexcept unless any destructor for a potentially constructed sub-object is potentially throwing.

Assignment operators

Are implicitly noexcept unless any use of assignment within is potentially-throwing.


Here's some sample code that demonstrates the above (clang 6.0.0, gcc 8.0.0):

int foo() { return 1; }
int bar() noexcept{ return 1; }

struct Foo{};

struct Bar{
 Bar(){}
};

int main()
{
    static_assert(noexcept(bar()));
    static_assert(!noexcept(foo()));
    static_assert(noexcept(Foo()));
    static_assert(noexcept(Foo().~Foo()));
    static_assert(noexcept(Foo().operator=(Foo())));
    static_assert(!noexcept(Bar()));
    Bar b;
    static_assert(noexcept(b.~Bar()));
}

Yet another reason to use =default or allow the compiler to generate its own versions of your special member functions via omission.

Related