类的函数声明后面的"default"是什么意思?

299

我看到default被用在类中函数声明的旁边。它是做什么用的?

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

38
在赋值运算符声明中,位于 "=" 前面的 "&" 符号是什么作用? - dshin
11
@dshin 这是一个成员函数的引用限定符说明。 - Kane
5个回答

343

这是一个C++11的新特性

它意味着你想要使用编译器生成的函数版本,所以你不需要指定函数体。

你也可以使用= delete来指定你不想要自动生成该函数。

随着移动构造函数和移动赋值操作符的引入,自动生成构造函数、析构函数和赋值操作符的规则变得非常复杂。使用=default=delete可以使事情变得更容易,因为你不需要记住这些规则:你只需要说明你想发生什么。


29
= delete 更加严格:它意味着使用该函数是被禁止的,尽管它仍然参与重载决议。 - Deduplicator
11
如果我们想要使用编译器生成的定义,那么我们是不是应该跳过编写该函数,而是直接将其分配为默认值,而不是“先编写它,然后再将其分配为默认值”? - Mayank Jindal
4
因为如果我们已经提供了参数化构造函数,而没有提供无参构造函数,编译器就会报错。在这种情况下,没有办法跳过默认构造函数的声明。 - girish
1
“compiler-generated version of that function” 这句话是指由编译器生成的函数版本。这是否意味着一个空的函数体? - PhiloRobotist

58

这是一个新的C++0x特性,它告诉编译器创建相应构造函数或赋值运算符的默认版本,即只为每个成员执行复制或移动操作。这很有用,因为移动构造函数并不总是默认生成的(例如,如果您有自定义析构函数),与复制构造函数不同(类似于赋值运算符)。但如果没有什么非常规的东西需要写,最好让编译器处理它,而不是每次都自己拼写。

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

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

详情请参见标准的第12.8节。


7
这不仅适用于构造函数和赋值操作,也适用于 operator new/new[]operator delete/delete[] 及其重载。 - Sebastian Mach

23

这是C++11中的新特性,详见这里。如果你定义了一个构造函数,但想要使用默认值来定义其他构造函数,则此功能可能非常有用。在C++11之前,一旦定义了一个构造函数,即使它们等同于默认值,也必须定义所有构造函数。

还要注意,在某些情况下,不可能提供用户定义的默认构造函数,使其在默认值初始化值初始化下与编译器生成的构造函数表现相同。使用default关键字可以让你重新获得这个行为。


8
关于第二段,你能提供一个例子吗? - John Smith

12

这个用例在其他答案中似乎没有提到,它可以轻松地让您更改构造函数的可见性。例如,也许您希望友元类能够访问复制构造函数,但您不希望该构造函数公开可用。


3

C++17 N4659标准草案

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

1 A function definition of the form:

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

is called an explicitly-defaulted definition. A function that is explicitly defaulted shall

  • (1.1) — be a special member function,

  • (1.2) — have the same declared function type (except for possibly differing ref-qualifiers and except that in the case of a copy constructor or copy assignment operator, the parameter type may be “reference to non-const T”, where T is the name of the member function’s class) as if it had been implicitly declared, and

  • (1.3) — not have default arguments.

2 An explicitly-defaulted function that is not defined as deleted may be declared constexpr only if it would have been implicitly declared as constexpr. If a function is explicitly defaulted on its first declaration, it is implicitly considered to be constexpr if the implicit declaration would be.

3 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.

4 [ Example:

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

— end example ]

5 Explicitly-defaulted functions and implicitly-declared functions are collectively called defaulted functions, and the implementation shall provide implicit definitions for them (15.1 15.4, 15.8), which might mean defining them as deleted. A function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed. [ Note: Declaring a function as defaulted after its first declaration can provide efficient execution and concise definition while enabling a stable binary interface to an evolving code base. — end note ]

6 [ Example:

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

— end example ]

然后问题当然是哪些函数可以被隐式声明,以及何时发生这种情况,我已经在以下链接中解释过了:

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接