• Advertisement
Sign in to follow this  

noexcept

Recommended Posts

C++ destructors are noexcept by default, but what about:

struct A {
	A()                     = default; // noexcept?
	A(const A &)            = default; // noexcept?
	A(A &&)                 = default; // noexcept?
	virtual ~A()            = default; // noexcept
	A &operator=(const A &) = default; // noexcept?
	A &operator=(A &&)      = default; // noexcept?
...
};

struct B : A {
	B()                     = default; // noexcept?
	B(const B &)            = default; // noexcept?
	B(B &&)                 = default; // noexcept?
	virtual ~B()            = default; // noexcept
	B &operator=(const B &) = default; // noexcept?
	B &operator=(B &&)      = default; // noexcept?
...
};

 

Edited by matt77hias

Share this post


Link to post
Share on other sites
Advertisement

The answer is a bit complicated. The definitive answer is in the standard itself, under Exception Specifications and has 17 clauses.

Clauses 12-15 involve the implicitly created functions, but I think the keys to your question are here (from C++11)

=====

5 If a virtual function has an exception-specification, all declarations, including the definition, of any function that overrides that virtual function in any derived class shall only allow exceptions that are allowed by the exception-specification of the base class virtual function. A similar restriction applies to assignment to and initialization of pointers to functions, pointers to member functions, and references to functions: the target entity shall allow at least the exceptions allowed by the source value in the assignment or initialization. 

...

14 An implicitly declared special member function (Clause 12) shall have an exception-specification. If f is an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f’s implicit definition; f shall allow all exceptions if any function it directly invokes allows all exceptions, and f shall allow no exceptions if every function it directly invokes allows no exceptions. [emphasis added]

15 A deallocation function (3.7.4.2) with no explicit exception-specification is treated as if it were specified with noexcept(true).

=====

C++17 slightly modified the rule regarding destructors, the rule is reduced if it must call a base class function or member object with a destructor that specifies noexcept(false).

So as I read that, if class A has a specification, class B must be at least as restrictive.  After that, it depends on the base class and members of the class.

 

As a general rule, exception specifiers in C++ are avoided, especially in games.  This is very different from Java where they mean something quite different.  Exception specifiers add a cost to the function as they cause try/catch blocks to be added, and if the expectation is violated the program terminates with no alternate recourse, even if you had exception handlers for it elsewhere in the chain. There are no compiler errors or warnings required even if the compiler can see the glaringly obvious problem. 

 

Share this post


Link to post
Share on other sites

@frob You should definitely not avoid noexcept, but the opposite (throw).

Quote

In a noexcept function, optimizers need not keep the runtime stack in an unwindable state if an exception would propagate out of the function, nor must they ensure that objects in a noexcept function are destroyed in the inverse order of construction should an exception leave the function. Functions with throw() exception specifications lack such optimization flexibility, as do functions with no exception specification at all.

Scott Meyers: Modern Effective C++

Furthermore, some std functions will refuse to use a non-noexcept move constructor and use the copy constructor instead.

Quote

std::vector::push_back takes advantage of this “move if you can, but copy if you must” strategy, and it’s not the only function in the Standard Library that does. Other functions sporting the strong exception safety guarantee in C++98 (e.g., std::vector::reserve, std::deque::insert, etc.) behave the same way. All these functions replace calls to copy operations in C++98 with calls to move operations in C++11 only if the move operations are known to not emit exceptions. But how can a function know if a move operation won’t produce an exception? The answer is obvious: it checks to see if the operation is declared noexcept.

The checking is typically rather roundabout. Functions like std::vector::push_back call std::move_if_noexcept, a variation of std::move that conditionally casts to an rvalue, depending on whether the type’s move constructor is noexcept. In turn, std::move_if_noexcept consults std::is_nothrow_move_constructible, and the value of this type trait is set by compilers, based on whether the move constructor has a noexcept (or throw()) designation.

Scott Meyers: Modern Effective C++

Edited by matt77hias

Share this post


Link to post
Share on other sites
1 hour ago, frob said:

So as I read that, if class A has a specification

That explains struct B, but what about struct A? Does it depend on the constructors of the member variables of A?

Edited by matt77hias

Share this post


Link to post
Share on other sites

It looks like they're based on the members themselves.  That's the part I bolded. If any of the member's similar functions allow all exceptions, the generated ones will too. If all of the members allow no exceptions, it will match.

So if all the assignment operators forbid them then the implicit one will also forbid it.  Otherwise they'll be allowed.

Share this post


Link to post
Share on other sites
6 minutes ago, frob said:

It looks like they're based on the members themselves.  That's the part I bolded. If any of the member's similar functions allow all exceptions, the generated ones will too. If all of the members allow no exceptions, it will match.

So to summarize, you should get the desired behavior without adding noexcept explicitly to these "special" functions in a "pure" C++ context.

On the other hand, you should actually add them explicitly to have total control, since you can call C functions or Windows functions. C functions cannot throw exceptions, Windows functions can throw exceptions, but those are different from std::exception.

Still thank you for pointing to the paragraphs of the standard.

Edited by matt77hias

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Advertisement