我知道,而不是写:
class A {
public:
A(A&&) noexcept = default;
};
一个人最好写
class A {
public:
A(A&&) noexcept;
};
inline A::A(A&&) noexcept = default;
我听到的原因是:
它避免了构造函数变为deleted
。如果无法定义函数,编译器将给出错误。
即使某些成员字段的move构造函数未使用noexcept
注释,也将声明move构造函数noexcept
。
有人可以进一步解释差异背后的理论吗?
答案 0 :(得分:11)
仅声明用于描述类/方法,所以在做时
class A {
public:
A(A&&) noexcept;
};
您甚至可以根据需要实现A::A(A&&)
(定义可以位于不同的TU中)
通过以下方式实现它:
A::A(A&&) noexcept = default;
编译器必须生成方法(它不能确定是否存在声明精确方法时是否隐式删除了该方法),并提供诊断(如果不能)。
但是当您在类中声明它时:
class A {
public:
A(A&&) noexcept = default;
};
它是声明的“一部分”。因此它可能会被隐式删除(由于成员或基类)。
与noexcept
相同。
将定义放入专用TU的另一个优点是,所需依赖项的定义只能在该TU中,而不是在生成方法的每个位置。 (例如,用于pimpl习语)。
拆分定义和声明的一个缺点是该方法现在是“用户提供的”,可能会影响trivially_constructible / copyable /...
答案 1 :(得分:5)
该行为在[dcl.fct.def.default]p3中进行了说明:
如果显式默认的函数是用nostrong-specifier声明的,则不会产生相同的结果 异常说明作为隐式声明(18.4),然后
(3.1)— 如果该函数在其第一个声明中被明确默认,则定义为已删除;
(3.2)-否则,程序格式错误。
请注意,在这种情况下,措词更改了in C++20,但intent is the same更改了。我发现C ++ 17措辞更容易理解。
例如:
struct S {
S( S&& ) noexcept(false) = default;
};
由于[except.spec]p7,移动构造函数被定义为已删除:
X类的隐式声明的构造函数,或者没有默认的noexcept-specifier的构造函数 在且仅当以下任何一项时,在其第一个声明中具有可能抛出的异常规范 构造可能会被抛出:
(7.1)—通过重载分辨率在类X的构造函数的隐式定义中选择的构造函数,以 初始化可能构造的子对象,或者
(7.2)-这种初始化的子表达式,例如默认参数表达式或,
(7.3)—对于默认构造函数,是默认成员初始化程序。
没有一个案件。
如果我们回到 [dcl.fct.def.default] p3 ,则表示程序是格式错误的。格式错误的程序需要诊断,因此,如果我们将第一个示例修改如下(see it live):
struct S {
S( S&& ) noexcept(false) ;
private:
int i;
};
S::S( S&&) noexcept(false) = default ;
它将产生诊断信息,例如:
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 ;
^
请注意clang bug related to this case,看来他们没有关注Defect Report 1778。
您可能需要注意Declaring a function as defaulted after its first declaration,其中涵盖了一些优化/界面问题。