admin管理员组

文章数量:1124530

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?

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?

Share Improve this question edited 2 days ago Konvt asked 2 days ago KonvtKonvt 1277 bronze badges 4
  • 4 Behaviour is well defined. The default parameter is resolved at the call site, before the B constructor is invoked. The exception is thrown. – user4581301 Commented 2 days ago
  • 1 Remember that function arguments are passed by value. That means they're all evaluated before the function is called. – Barmar Commented 2 days ago
  • 1 Throwing an exception from a noexcept function generally doesn't lead to Undefined Behavior. It only means that std::terminate gets called. – Michael Mahn Commented yesterday
  • @Michael Mahn — moreover, this exception can be caught in a usual way. noexcept is nothing but a declaration. – Sergey A Kryukov Commented yesterday
Add a comment  | 

2 Answers 2

Reset to default 4

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]

In C++, the noexcept keyword is used to declare that a function is guaranteed not to throw exceptions. This guarantee allows the compiler to optimize code more effectively and provides clearer expectations about a function's behavior, especially in contexts like move operations within standard library containers.

When applying noexcept, it's important to differentiate between exceptions that occur within the function body and those that happen during parameter passing. The noexcept specification only applies to the function's internal operations. For instance, in the provided code, the constructor of B is marked as noexcept, which is safe because any potential exceptions from constructing the default parameter A(true) occur before the constructor body is executed and do not violate the noexcept guarantee of B's constructor itself.

Best practices for using noexcept include ensuring that all operations within a function are exception-free before marking it as noexcept. This is particularly recommended for move constructors and move assignment operators to enhance the performance of standard library containers like std::vector. Additionally, using conditional noexcept in templates can provide flexibility by allowing exception guarantees to depend on template parameters. However, incorrectly marking a function that might throw exceptions as noexcept can lead to unexpected program termination through std::terminate, so careful consideration and thorough testing are essential.

本文标签: cCan a function be marked noexcept if it takes a value type that may throwStack Overflow