c++c++11exceptionundefined-behaviornoexcept

Can a function be marked noexcept if it takes a value type that may throw?


In C++, I mark a function as noexcept in the following cases:

The function itself does not throw exceptions, but its value type parameters may throw exceptions when constructing.

The following code demonstrates that situation:

struct A {
    A(bool will_throw) {
        std::cerr << "Ctor of A\n";
        if (will_throw) throw std::runtime_error("An exception from A");
    }
    A(A&& lhs) noexcept {
        std::cerr << "Move ctor of A\n";
    }
    ~A() noexcept = default;
};

struct B {
    A mem_;

    // Exceptions are thrown during parameter passing, not inside the function
    // Thus it isn't an UB I think.
    B(A value = A(true)) noexcept : mem_{std::move(value)} {/* do nothing */}
    ~B() noexcept = default;
};

In this code, the constructor of B is marked as noexcept, and the default A(true) constructor may throw an exception, but I believe this does not lead to undefined behavior since the exception occurs during parameter passing and not within the body of the function.

The test program is here.

My question is:

In similar cases, is it safe to mark the constructor and other functions with noexcept? Can this practice be applied broadly, especially in cases where the function itself does not throw any exceptions?


Solution

  • Trying to propagate an exception out of a noexcept function doesn't cause UB. It causes std::terminate to be called.

    If you don't want std::terminate to be called, then it is still safe for a noexcept function to accept a parameter type whose initialization might throw. See [expr.call]/6

    [...] The initialization and destruction of each parameter occurs within the context of the full-expression ([intro.execution]) where the function call appears.
    [Example 2: The access ([class.access.general]) of the constructor, conversion functions, or destructor is checked at the point of call. If a constructor or destructor for a function parameter throws an exception, any function-try-block ([except.pre]) of the called function with a handler that can handle the exception is not considered. — end example]