=声明与定义中的默认值

时间:2018-07-14 15:13:17

标签: c++ default-constructor noexcept

我知道,而不是写:

class A {
public:
    A(A&&) noexcept = default;
};

一个人最好写

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

inline A::A(A&&) noexcept = default;

我听到的原因是:

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

  2. 即使某些成员字段的move构造函数未使用noexcept注释,也将声明move构造函数noexcept

有人可以进一步解释差异背后的理论吗?

2 个答案:

答案 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,其中涵盖了一些优化/界面问题。