What are the side effects of marking a derived implementation noexcept while the interface is not

Viewed 269

We have a class which implements IUnknown (or any interface which we don't own). We started marking most/all of our methods with noexcept for any potential optimization since we don't throw any exceptions anyway; although some of the libraries we depend on may. The question was raised whether or not QueryInterface/AddRef/Release should be marked noexcept since the interface isn't.

Are there any side effects or gotchas when only some of the derived classes are marked noexcept?

2 Answers

You should be careful with noexcept in general. Unless the compiler can prove that the function really won't throw any exceptions, then it must insert a dynamic handler to terminate your program in case of an exception. Thus it won't necessarily lead to the optimizations you're hoping for. In any case, adding it to AddRef, Release, and QueryInterface should be safe.

Edit

For example, consider the following code:

extern int Foo();

int Bar() noexcept
{
    return Foo();
}

This is what Clang 7.0 generates on O3:

Bar():                                # @Bar()
    push    rax
    call    Foo()
    pop     rcx
    ret
    mov     rdi, rax
    call    __clang_call_terminate
__clang_call_terminate:                 # @__clang_call_terminate
    push    rax
    call    __cxa_begin_catch
    call    std::terminate()

If you delete the noexcept, you get this instead:

Bar():                                # @Bar()
    jmp     Foo()                 # TAILCALL

In this example, the primary effect is just to bloat the image a little bit, but notice that the call to Foo also became a little less efficient.

I would always recommend adding noexcept wherever you can.

If you add it to a location and crash because of it, you've just learned that you can't use it there along with the reason why.

Related