I know that instead of writing:
class A {
public:
A(A&&) noexcept = default;
};
One should better write
class A {
public:
A(A&&) noexcept;
};
inline A::A(A&&) noexcept = default;
The reasons I've heard are:
It avoids the constructor becomes deleted
. Compiler will give an error if it is unable to define the function.
The move constructor is declared noexcept
even if some of the member fields' move constructor are not annotated with noexcept
.
Could someone explain a bit more about the theory behind the differences?
Definition allocates memory to an entity. A declaration does not allocate memory to the entities.
Declaration tells the compiler about the existence of an entity in the program and its location. When you declare a variable, you should also initialize it. Initialization is the process of assigning a value to the Variable. Every programming language has its own method of initializing the variable.
Definitions of declaration of defaulta formal statement from a creditor (=someone owed money) that the debtor (=person owing money) has not done something they are obligated to do, suc as making payment.
Declaration means that variable is only declared and memory is allocated, but no value is set. However, definition means the variables has been initialized. The same works for variables, arrays, collections, etc.
Only declaration is used to describe the class/method, so when doing
class A {
public:
A(A&&) noexcept;
};
You might even implement A::A(A&&)
as you want (definition can be in different TU)
When you implement it with:
A::A(A&&) noexcept = default;
Compiler has to generate the method (it cannot tell if it is implicitly deleted as declaration precise method exists), and provides diagnostic if it can't.
But when you declare it inside the class:
class A {
public:
A(A&&) noexcept = default;
};
It is "part" of declaration. so it might be implicitly deleted (because of member or base class).
Same apply for noexcept
.
An other advantage to put definition in dedicated TU, it that definition of required dependencies can be only in that TU, instead of each place where the method would be generated. (Useful for pimpl idiom for example).
One disadvantage of split definition and declaration is that the method is now "user provided", that may affect traits as trivially_constructible/copyable/...
The behavior is covered in [dcl.fct.def.default]p3 which says:
If a function that is explicitly defaulted is declared with a noexcept-specifier that does not produce the same exception specification as the implicit declaration (18.4), then
(3.1) — if the function is explicitly defaulted on its first declaration, it is defined as deleted;
(3.2) — otherwise, the program is ill-formed.
Note the wording changes in C++20 but the intent is the same for this case. I find the C++17 wording simpler to grok.
For example given:
struct S {
S( S&& ) noexcept(false) = default;
};
The move constructor is defined as deleted since due to [except.spec]p7:
An implicitly-declared constructor for a class X, or a constructor without a noexcept-specifier that is defaulted on its first declaration, has a potentially-throwing exception specification if and only if any of the following constructs is potentially-throwing:
(7.1) — a constructor selected by overload resolution in the implicit definition of the constructor for class X to initialize a potentially constructed subobject, or
(7.2) — a subexpression of such an initialization, such as a default argument expression, or,
(7.3) — for a default constructor, a default member initializer.
none of the cases hold.
If we got back to [dcl.fct.def.default]p3 it says otherwise the program is ill-formed. Ill-formed programs require a diagnostic so if we modify the first example as follows (see it live):
struct S {
S( S&& ) noexcept(false) ;
private:
int i;
};
S::S( S&&) noexcept(false) = default ;
it will produce a diagnostic e.g.:
error: function 'S::S(S&&)' defaulted on its redeclaration with an exception-specification that differs from the implicit exception-specification 'noexcept'
S::S( S&&) noexcept(false) = default ;
^
Note clang bug related to this case, it seems they are not following Defect Report 1778.
You may want to note Declaring a function as defaulted after its first declaration which covers some the optimization/interface issues.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With