Implicit exception specification differs for inline and out-of-line definition?
Asked Answered
N

1

5

Consider the following example:

#include <iostream>

struct X
{
    X() = default;
    X(const X&) = default;
    X(X&&) = default;
    X& operator = (const X&) = default;
    X& operator = (X&&) = default;
};

int main()
{
    static_assert(std::is_nothrow_move_assignable_v<X>);

    return 0;
}

The move assignment operator appears to be implicitly noexcept, since the static assertion passes.

However, defining it out-of-line makes the assertion fail:

#include <iostream>

struct X
{
    X() = default;
    X(const X&) = default;
    X(X&&) = default;
    X& operator = (const X&) = default;
    X& operator = (X&&); //= default;
};

X& X::operator=(X&&) = default;

int main()
{
    static_assert(std::is_nothrow_move_assignable_v<X>);

    return 0;
}

Why do they behave differently?

Nemertean answered 10/10, 2023 at 15:28 Comment(1)
X& operator = (X&&); is not noexcept. The fact you later use =default to simplify implementation does not matter. Definition cannot change declaration.Sclar
E
6

In your second code snippet, the move-assignment function is actually user-provided, because you have (explicitly) declared it – but not defaulted it – in its first declaration.

From this Draft C++17 Standard (bold emphasis mine):

11.4.2 Explicitly-defaulted functions        [dcl.fct.def.default]



5    … A function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed.

To get the same behaviour (i.e., to make that 'defaulted' function noexcept) as in the first snippet, but without specifying the = default in the declaration, you will need to explicitly add the noexcept attribute, both to the declaration and to the later definition:

struct X
{
    X() = default;
    X(const X&) = default;
    X(X&&) = default;
    X& operator = (const X&) = default;
    X& operator = (X&&) noexcept; // Add "noxcept" here ...
};


X& X::operator=(X&&) noexcept = default; // ... AND here!

int main()
{
    static_assert(std::is_nothrow_move_assignable_v<X>);

    return 0;
}
Eskridge answered 10/10, 2023 at 16:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.