Was bedeutet "Standard" nach der Funktionsdeklaration einer Klasse?


221

Ich habe defaultneben Funktionsdeklarationen in einer Klasse verwendet gesehen. Was tut es?

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

26
Was bewirkt das "&" vor dem "=" in den Deklarationen des Zuweisungsoperators?
Dshin

Antworten:


249

Es ist eine neue C ++ 11-Funktion .

Dies bedeutet, dass Sie die vom Compiler generierte Version dieser Funktion verwenden möchten, sodass Sie keinen Body angeben müssen.

Sie können auch verwenden , = deleteum anzugeben , dass Sie nicht wollen , dass der Compiler diese Funktion automatisch zu generieren.

Mit der Einführung von Verschiebungskonstruktoren und Verschiebungszuweisungsoperatoren sind die Regeln für die Generierung automatischer Versionen von Konstruktoren, Destruktoren und Zuweisungsoperatoren recht komplex geworden. Verwenden = defaultund = deleteerleichtert die Arbeit, da Sie sich nicht an die Regeln erinnern müssen: Sie sagen nur, was passieren soll.


17
= deleteist stärker: Es bedeutet, dass die Verwendung dieser Funktion verboten ist, obwohl sie immer noch an der Überlastungsauflösung beteiligt ist.
Deduplikator

2
Aber wenn wir die Compiler-Generierungsdefinition verwenden möchten, sollten wir dann nicht das Schreiben dieser Funktion überspringen, anstatt "sie zuerst zu schreiben und sie dann der Standardeinstellung zuzuweisen"?
Mayank Jindal

47

Dies ist eine neue C ++ 0x-Funktion, die den Compiler anweist, die Standardversion des jeweiligen Konstruktors oder Zuweisungsoperators zu erstellen, dh die, die nur die Kopier- oder Verschiebeaktion für jedes Mitglied ausführt. Dies ist nützlich, da der Verschiebungskonstruktor im Gegensatz zum Kopierkonstruktor (und ebenfalls für die Zuweisung) nicht immer standardmäßig generiert wird (z. B. wenn Sie einen benutzerdefinierten Destruktor haben). Wenn jedoch nichts nicht triviales zu schreiben ist, ist es besser, das zuzulassen Der Compiler handhabt es, als es jedes Mal selbst zu formulieren.

Beachten Sie auch, dass ein Standardkonstruktor nicht generiert wird, wenn Sie einen anderen nicht standardmäßigen Konstruktor angeben. Wenn Sie auch weiterhin den Standardkonstruktor möchten, können Sie diese Syntax verwenden, damit der Compiler einen erstellt.

Als weiteren Anwendungsfall gibt es mehrere Situationen, in denen ein Kopierkonstruktor nicht implizit generiert wird (z. B. wenn Sie einen benutzerdefinierten Verschiebungskonstruktor bereitstellen). Wenn Sie weiterhin die Standardversion möchten, können Sie diese mit dieser Syntax anfordern.

Einzelheiten finden Sie in Abschnitt 12.8 des Standards.


5
Obwohl es nicht nur für den Bau und Zuordnungen, sondern gilt auch für operator new/new[], operator delete/delete[]und deren Überlastung.
Sebastian Mach

21

Es ist neu in C ++ 11, siehe hier . Dies kann sehr nützlich sein, wenn Sie einen Konstruktor definiert haben, aber die Standardeinstellungen für die anderen verwenden möchten. Vor C ++ 11 müssten Sie alle Konstruktoren definieren, sobald Sie einen definiert haben, auch wenn sie den Standardeinstellungen entsprechen.

Beachten Sie auch , dass in bestimmten Situationen ist es unmöglich, einen benutzerdefinierten Standardkonstruktor zur Verfügung zu stellen , dass verhält sich die gleichen wie die Compiler eine sowohl unter synthetisierte Standard und Wert der Initialisierung. defaultermöglicht es Ihnen, dieses Verhalten zurückzubekommen.


5
Können Sie zum zweiten Absatz ein Beispiel geben?
John Smith

11

Ein weiterer Anwendungsfall, den ich in diesen Antworten nicht sehe, besteht darin, dass Sie die Sichtbarkeit eines Konstruktors leicht ändern können. Zum Beispiel möchten Sie vielleicht, dass eine Freundesklasse auf den Kopierkonstruktor zugreifen kann, aber Sie möchten nicht, dass er öffentlich verfügbar ist.


1

C ++ 17 N4659 Standardentwurf

https://github.com/cplusplus/draft/blob/master/papers/n4659.pdf 11.4.2 "Explizit voreingestellte Funktionen":

1 Eine Funktionsdefinition des Formulars:

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

wird als explizit voreingestellte Definition bezeichnet. Eine explizit voreingestellte Funktion soll

  • (1.1) - eine spezielle Mitgliedsfunktion sein,

  • (1.2) - haben denselben deklarierten Funktionstyp (mit Ausnahme möglicherweise unterschiedlicher Referenzqualifizierer und mit der Ausnahme, dass im Fall eines Kopierkonstruktors oder eines Kopierzuweisungsoperators der Parametertyp "Referenz auf Nicht-Konstante T" sein kann, wobei T ist den Namen der Klasse der Mitgliedsfunktion), als ob sie implizit deklariert worden wäre, und

  • (1.3) - keine Standardargumente haben.

2 Eine explizit voreingestellte Funktion, die nicht als gelöscht definiert ist, kann nur dann als constexpr deklariert werden, wenn sie implizit als constexpr deklariert worden wäre. Wenn eine Funktion bei ihrer ersten Deklaration explizit voreingestellt ist, wird sie implizit als constexpr betrachtet, wenn die implizite Deklaration wäre.

3 Wenn eine explizit voreingestellte Funktion mit einem noexcept-Spezifizierer deklariert wird, der nicht dieselbe Ausnahmespezifikation wie die implizite Deklaration (18.4) erzeugt, dann

  • (3.1) - Wenn die Funktion bei ihrer ersten Deklaration explizit voreingestellt ist, wird sie als gelöscht definiert.

  • (3.2) - Andernfalls ist das Programm fehlerhaft.

4 [Beispiel:

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

- Beispiel beenden]

5 Explizit voreingestellte Funktionen und implizit deklarierte Funktionen werden gemeinsam als voreingestellte Funktionen bezeichnet, und die Implementierung muss implizite Definitionen für sie bereitstellen (15.1 15.4, 15.8), was bedeuten kann, dass sie als gelöscht definiert werden. Eine Funktion wird vom Benutzer bereitgestellt, wenn sie vom Benutzer deklariert und bei ihrer ersten Deklaration nicht explizit als Standard festgelegt oder gelöscht wurde. Eine vom Benutzer bereitgestellte explizit voreingestellte Funktion (dh explizit voreingestellte Funktion nach ihrer ersten Deklaration) wird an dem Punkt definiert, an dem sie explizit voreingestellt ist. Wenn eine solche Funktion implizit als gelöscht definiert ist, ist das Programm fehlerhaft. [Hinweis: Das Deklarieren einer Funktion als Standard nach ihrer ersten Deklaration kann eine effiziente Ausführung und präzise Definition ermöglichen und gleichzeitig eine stabile binäre Schnittstelle zu einer sich entwickelnden Codebasis ermöglichen. - Endnote]

6 [Beispiel:

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

- Beispiel beenden]

Dann ist natürlich die Frage, welche Funktionen implizit deklariert werden können und wann dies geschieht, was ich unter erklärt habe:

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.