=声明与定义中的默认值

lai*_*e9m 18 c++ default-constructor noexcept

我知道这不是写作:

class A {
public:
    A(A&&) noexcept = default;
}?
Run Code Online (Sandbox Code Playgroud)

一个人应该写得更好

class A {
public:
    A(A&&) noexcept;
}?

inline A::A(A&&) noexcept = default;
Run Code Online (Sandbox Code Playgroud)

我听到的原因是:

  1. 它避免了构造函数变成了deleted.如果无法定义函数,编译器将给出错误.

  2. noexcept即使某些成员字段的移动构造函数未注释,也会声明移动构造函数noexcept.

有人可以解释一下这些差异背后的理论吗?

Jar*_*d42 11

只有声明用于描述类/方法,所以在做的时候

class A {
public:
    A(A&&) noexcept;
}?
Run Code Online (Sandbox Code Playgroud)

您甚至A::A(A&&)可以根据需要实现(定义可以在不同的TU中)

当您使用以下方法实现它:

A::A(A&&) noexcept = default;
Run Code Online (Sandbox Code Playgroud)

编译器必须生成该方法(它无法判断它是否因为存在声明精确方法而被隐式删除),并且如果不能则提供诊断.

但是当你在类中声明它时:

class A {
public:
    A(A&&) noexcept = default;
}?
Run Code Online (Sandbox Code Playgroud)

它是宣言的"一部分".所以它可能被隐式删除(因为成员或基类).

同样适用noexcept.

将定义放在专用TU中的另一个好处是,所需依赖关系的定义只能在那个TU中,而不是每个生成方法的地方.(例如对于pimpl成语很有用).

拆分定义和声明的一个缺点是该方法现在是"用户提供的",可能会影响travits trivially_constructible/copyable/...


Sha*_*our 5

该行为在[dcl.fct.def.default] p3有所说明:

如果使用noexcept-specifier声明显式默认的函数,该函数不会产生与隐式声明相同的异常规范(18.4),那么

(3.1) - 如果函数在其第一个声明中明确默认,则将其定义为已删除;

(3.2) - 否则,该程序是不正确的.

请注意C++ 20中的措辞变化,但这种情况的意图是相同的.我发现C++ 17的措辞比较简单.

例如给出:

struct S {
      S( S&& ) noexcept(false) = default;
};
Run Code Online (Sandbox Code Playgroud)

由于[except.spec] p7,移动构造函数被定义为已删除:

对于类X的隐式声明的构造函数,或者没有在其第一个声明中默认的noexcept-specifier的构造函数,当且仅当以下任何构造可能被抛出时,才会有一个潜在抛出的异常规范:

(7.1) - 在类X的构造函数的隐式定义中通过重载决策选择的构造函数,用于初始化可能构造的子对象,或者

(7.2) - 这种初始化的子表达式,例如默认参数表达式,或者,

(7.3) - 对于默认构造函数,默认成员初始值设定项.

没有一个案件成立.

如果我们回到[dcl.fct.def.default] p3,它会说该程序格式不正确.错误的程序需要诊断,所以如果我们修改第一个例子如下(现场直播):

struct S {
   S( S&& ) noexcept(false) ;
private:
  int i;

};

S::S( S&&) noexcept(false) = default ;
Run Code Online (Sandbox Code Playgroud)

它会产生诊断,例如:

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 ;
 ^
Run Code Online (Sandbox Code Playgroud)

注意与此案相关的clang bug,似乎他们没有遵循1778的缺陷报告.

您可能需要注意在第一次声明后将声明声明为默认值,其中包含一些优化/接口问题.