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.