类的函数声明后的“默认”是什么意思?


221

我已经看到default在类中的函数声明旁边使用过。它有什么作用?

class C {
  C(const C&) = default;
  C(C&&) = default;
  C& operator=(const C&) & = default;
  C& operator=(C&&) & = default;
  virtual ~C() { }
};

26
赋值运算符声明中位于“ =”之前的“&”有什么作用?
dshin

6
@dshin这是成员函数ref限定
凯恩'18

Answers:


249

这是C ++ 11新功能

这意味着您要使用该函数的编译器生成版本,因此无需指定主体。

您还可以= delete用于指定您希望编译器自动生成该函数。

随着移动构造函数和移动赋值运算符的引入,生成自动版本的构造函数,析构函数和赋值运算符的规则变得非常复杂。使用= default= delete使事情变得容易,因为您无需记住规则:您只需说出要发生的事情即可。


17
= delete更强大:这意味着,尽管仍然参与重载解析,但禁止使用该功能。
Deduplicator 2015年

2
但是,如果我们要使用编译器生成定义,那么我们不应该跳过编写该函数而不是“先编写然后将其分配给默认值”吗?
Mayank Jindal

47

这是C ++ 0x的一项新功能,可告诉编译器创建相应构造函数或赋值运算符的默认版本,即仅对每个成员执行复制或移动动作的版本。这很有用,因为移动构造函数并非总是默认生成的(例如,如果您有一个自定义析构函数),与复制构造函数(以及类似的赋值方法)不同,但是如果没有非平凡的东西要写,最好让编译器会处理它,而不是每次自己将其拼写出来。

还要注意,如果提供任何其他非默认构造函数,则不会生成默认构造函数。如果仍然需要默认的构造函数,则可以使用此语法让编译器使用它。

作为另一个用例,在某些情况下不会隐式生成副本构造函数(例如,如果您提供自定义的移动构造函数)。如果仍然需要默认版本,则可以使用此语法进行请求。

有关详细信息,请参见标准的12.8节。


5
虽然它不仅是建设者和分配,同时也适用于operator new/new[]operator delete/delete[]和他们的过载。
塞巴斯蒂安·马赫

21

它是C ++ 11中的新功能,请参见此处。如果您定义了一个构造函数,但想对其他构造函数使用默认值,则它会非常有用。在C ++ 11之前的版本中,您必须定义所有构造函数,即使它们等同于默认构造函数。

还要注意,在某些情况下,不可能提供用户定义的默认构造函数,该默认构造函数的行为与编译器在默认初始化下综合的构造函数相同。default使您能够恢复这种行为。


5
关于第二段,您能提供一个例子吗?
约翰·史密斯,

11

我在这些答案中没有看到的另一个用例是,它很容易让您更改构造函数的可见性。例如,也许您希望一个朋友类能够访问复制构造函数,但是您不希望它公开。


1

C ++ 17 N4659标准草案

https://github.com/cplusplus/draft/blob/master/papers/n4659.pdf 11.4.2“显式默认函数”:

1形式的函数定义:

attribute-specifier-seq opt decl-specifier-seq opt declarator virt-specifier-seq opt = default ;

被称为显式默认定义。明确默认的功能应

  • (1.1)—是特殊的成员函数,

  • (1.2)—具有相同的声明函数类型(除了可能不同的ref限定符,以及对于复制构造函数或复制赋值运算符而言,参数类型可以为“对非常量T的引用”,其中T为成员函数的类的名称),就好像它已隐式声明一样,并且

  • (1.3)-没有默认参数。

2如果未隐式声明为constexpr,则未定义为delete的显式默认函数只能声明为constexpr。如果一个函数在其第一个声明中被显式默认,则如果隐式声明是隐式的,则将其隐式视为constexpr。

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

  • (3.1)—如果该函数在其第一个声明中被显式默认,则将其定义为delete;

  • (3.2)—否则,程序格式错误。

4 [示例:

struct S {
  constexpr S() = default;            // ill-formed: implicit S() is not constexpr
  S(int a = 0) = default;             // ill-formed: default argument
  void operator=(const S&) = default; // ill-formed: non-matching return type
  ~ S() noexcept(false) = default;    // deleted: exception specification does not match
private:
  int i;                              // OK: private copy constructor
  S(S&);
};
S::S(S&) = default;                   // OK: defines copy constructor

—结束示例]

5显式默认函数和隐式声明函数统称为默认函数,实现应为其提供隐式定义(15.1 15.4、15.8),这可能意味着将它们定义为已删除。如果函数是由用户声明的,并且未在其第一个声明中显式默认或删除,则由用户提供。由用户提供的显式默认函数(即,在第一次声明后显式默认)在显式默认的位置定义;如果将此类函数隐式定义为Delete,则程序格式错误。[注意:在第一个声明之后将函数声明为默认函数可以提供有效的执行和简洁的定义,同时还可以为不断发展的代码库提供稳定的二进制接口。—尾注]

6 [示例:

struct trivial {
  trivial() = default;
  trivial(const trivial&) = default;
  trivial(trivial&&) = default;
  trivial& operator=(const trivial&) = default;
  trivial& operator=(trivial&&) = default;
  ~ trivial() = default;
};
struct nontrivial1 {
  nontrivial1();
};
nontrivial1::nontrivial1() = default;       // not first declaration

—结束示例]

然后的问题是,当然可以隐式声明哪些函数以及何时隐式声明,我已在以下文章中进行了解释:

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.