Copy assignment operator
Ein Kopierzuweisungsoperator ist eine nicht-templatbasierte nicht-statische Elementfunktion mit dem Namen operator = , die mit einem Argument desselben Klassentyps aufgerufen werden kann und den Inhalt des Arguments kopiert, ohne das Argument zu verändern.
Inhaltsverzeichnis |
Syntax
Für die formale Syntax des Kopierzuweisungsoperators siehe Funktionsdeklaration . Die folgende Syntaxliste zeigt nur einen Teil aller gültigen Syntaxformen des Kopierzuweisungsoperators.
Rückgabetyp
operator=(
Parameterliste
);
|
(1) | ||||||||
Rückgabetyp
operator=(
Parameterliste
)
Funktionsrumpf
|
(2) | ||||||||
Rückgabetyp
operator=(
Parameterliste-ohne-Standardwerte
) = default;
|
(3) | (seit C++11) | |||||||
Rückgabetyp
operator=(
Parameterliste
) = delete;
|
(4) | (seit C++11) | |||||||
Rückgabetyp
Klassenname
::
operator=(
Parameterliste
)
Funktionsrumpf
|
(5) | ||||||||
Rückgabetyp
Klassenname
::
operator=(
Parameterliste-ohne-Standardwerte
) = default;
|
(6) | (seit C++11) | |||||||
| class-name | - |
die Klasse, deren Kopierzuweisungsoperator deklariert wird, der Klassentyp wird als
T
in den folgenden Beschreibungen angegeben
|
| parameter-list | - |
eine
Parameterliste
mit nur einem Parameter, der vom Typ
T
,
T&
,
const
T
&
,
volatile
T
&
oder
const
volatile
T
&
ist
|
| parameter-list-no-default | - |
eine
Parameterliste
mit nur einem Parameter, der vom Typ
T
,
T&
,
const
T
&
,
volatile
T
&
oder
const
volatile
T
&
ist und keinen Standardargument hat
|
| function-body | - | der Funktionsrumpf des Kopierzuweisungsoperators |
| return-type | - |
jeder Typ, aber
T&
wird bevorzugt, um Verkettungen von Zuweisungen zu ermöglichen
|
Erklärung
struct X { X& operator=(X& other); // Kopierzuweisungsoperator X operator=(X other); // Pass-by-value ist erlaubt // X operator=(const X other); // Fehler: Falscher Parametertyp }; union Y { // Kopierzuweisungsoperatoren können andere Syntaxen als oben aufgeführt haben, // solange sie der allgemeinen Funktionsdeklarationssyntax folgen // und die oben aufgeführten Einschränkungen nicht verletzen auto operator=(Y& other) -> Y&; // OK: Nachgestellter Rückgabetyp Y& operator=(this Y& self, Y& other); // OK: Expliziter Objektparameter // Y& operator=(Y&, int num = 1); // Fehler: Enthält andere Nicht-Objektparameter };
Der Kopierzuweisungsoperator wird aufgerufen, sobald er durch die Überlagerungsauflösung ausgewählt wird, z.B. wenn ein Objekt auf der linken Seite eines Zuweisungsausdrucks erscheint.
Implizit deklarierter Kopierzuweisungsoperator
Wenn keine benutzerdefinierten Kopierzuweisungsoperatoren für einen Klassentyp bereitgestellt werden, deklariert der Compiler immer einen als inline public Mitglied der Klasse. Dieser implizit deklarierte Kopierzuweisungsoperator hat die Form T & T :: operator = ( const T & ) wenn alle folgenden Bedingungen zutreffen:
-
jede direkte Basis
BvonThat einen Kopierzuweisungsoperator, dessen ParameterBoder const B & oder const volatile B & sind; -
jedes nicht-statische Datenelement
MvonTvom Klassentyp oder Array von Klassentyp hat einen Kopierzuweisungsoperator, dessen ParameterModer const M & oder const volatile M & sind.
Andernfalls wird der implizit deklarierte Kopierzuweisungsoperator deklariert als T & T :: operator = ( T & ) .
Aufgrund dieser Regeln kann der implizit deklarierte Kopierzuweisungsoperator nicht an ein volatile Lvalue-Argument binden.
Eine Klasse kann mehrere Kopierzuweisungsoperatoren haben, z.B. sowohl T & T :: operator = ( T & ) als auch T & T :: operator = ( T ) . Wenn benutzerdefinierte Kopierzuweisungsoperatoren vorhanden sind, kann der Benutzer die Generierung des implizit deklarierten Kopierzuweisungsoperators mit dem Schlüsselwort default erzwingen. (seit C++11)
Der implizit deklarierte (oder bei seiner ersten Deklaration defaulted) Kopierzuweisungsoperator hat eine Ausnahmespezifikation, wie beschrieben in dynamischer Ausnahmespezifikation (bis C++17) noexcept-Spezifikation (seit C++17)
Da der Kopierzuweisungsoperator für jede Klasse immer deklariert wird, ist der Zuweisungsoperator der Basisklasse immer verborgen. Wenn eine using-declaration verwendet wird, um den Zuweisungsoperator aus der Basisklasse einzubringen, und sein Argumenttyp derselbe wie der Argumenttyp des impliziten Zuweisungsoperators der abgeleiteten Klasse sein könnte, wird die using-declaration ebenfalls durch die implizite Deklaration verborgen.
Implizit definierter Kopierzuweisungsoperator
Wenn der implizit deklarierte Kopierzuweisungsoperator weder gelöscht noch trivial ist, wird er (d.h. ein Funktionsrumpf wird generiert und kompiliert) vom Compiler definiert, falls ODR-used oder needed for constant evaluation (since C++14) . Für Union-Typen kopiert die implizit definierte Kopierzuweisung die Objektrepräsentation (wie durch std::memmove ). Für Nicht-Union-Klassentypen führt der Operator eine memberweise Kopierzuweisung der direkten Basisklassen und nicht-statischen Datenelemente des Objekts durch, in deren Initialisierungsreihenfolge, unter Verwendung von eingebauter Zuweisung für Skalare, memberweiser Kopierzuweisung für Arrays und Kopierzuweisungsoperator für Klassentypen (nicht-virtual aufgerufen).
|
Der implizit definierte Kopierzuweisungsoperator für eine Klasse
|
(seit C++14)
(bis C++23) |
|
Der implizit definierte Kopierzuweisungsoperator für eine Klasse
|
(seit C++23) |
|
Die Erzeugung des implizit definierten Kopierzuweisungsoperators ist veraltet, wenn
|
(since C++11) |
Gelöschter Copy-Zuweisungsoperator
Ein implizit deklarierter
oder explizit defaulted
(since C++11)
Kopierzuweisungsoperator für Klasse
T
ist
undefiniert
(until C++11)
als gelöscht definiert
(since C++11)
wenn eine der folgenden Bedingungen erfüllt ist:
-
Tbesitzt ein nicht-statisches Datenelement eines const-qualifizierten Nicht-Klassentyps (oder möglicherweise eines mehrdimensionalen Arrays davon). -
Tbesitzt ein nicht-statisches Datenelement eines Referenztyps. -
Tbesitzt ein potenziell konstruiertes Subobjekt des KlassentypsM(oder möglicherweise eines mehrdimensionalen Arrays davon), sodass die Überladungsauflösung bei der Suche nachMs Kopierzuweisungsoperator
-
- führt nicht zu einem verwendbaren Kandidaten, oder
- im Fall, dass das Unterobjekt ein variant member ist, wählt eine nicht-triviale Funktion aus.
|
Der implizit deklarierte Kopierzuweisungsoperator für Klasse
|
(seit C++11) |
Trivialer Kopierzuweisungsoperator
Der Kopierzuweisungsoperator für die Klasse
T
ist trivial, wenn alle folgenden Bedingungen zutreffen:
- es ist nicht vom Benutzer bereitgestellt (d. h., es ist implizit definiert oder standardmäßig);
-
That keine virtuellen Memberfunktionen; -
That keine virtuellen Basisklassen; -
der Kopierzuweisungsoperator, der für jede direkte Basis von
Tausgewählt ist, ist trivial; -
der Kopierzuweisungsoperator, der für jedes nicht-statische Klassentyp-Mitglied (oder Array von Klassentyp) von
Tausgewählt ist, ist trivial.
Ein trivialer Kopierzuweisungsoperator erstellt eine Kopie der Objektrepräsentation als ob durch std::memmove . Alle mit der C-Sprache kompatiblen Datentypen (POD-Typen) sind trivial kopierzuweisbar.
Qualifizierter Kopierzuweisungsoperator
|
Ein Kopierzuweisungsoperator ist geeignet, wenn er entweder benutzerdeklariert oder sowohl implizit deklariert als auch definierbar ist. |
(bis C++11) |
|
Ein Kopierzuweisungsoperator ist geeignet, wenn er nicht gelöscht ist. |
(seit C++11)
(bis C++20) |
|
Ein Kopierzuweisungsoperator ist geeignet, wenn alle folgenden Bedingungen erfüllt sind:
|
(seit C++20) |
Die Trivialität von qualifizierten Kopierzuweisungsoperatoren bestimmt, ob die Klasse ein trivially copyable type ist.
Hinweise
Wenn sowohl Kopier- als auch Verschiebezuweisungsoperatoren bereitgestellt werden, wählt die Überladungsauflösung die Verschiebezuweisung, wenn das Argument ein Rvalue ist (entweder ein Prvalue wie ein namenloser Temporärwert oder ein Xvalue wie das Ergebnis von std::move ), und wählt die Kopierzuweisung, wenn das Argument ein Lvalue ist (benanntes Objekt oder eine Funktion/ein Operator, die/dem eine Lvalue-Referenz zurückgibt). Wenn nur die Kopierzuweisung bereitgestellt wird, wählen alle Argumentkategorien diese (sofern sie ihr Argument als Wert oder als Referenz auf const entgegennimmt, da Rvalues an const-Referenzen binden können), was die Kopierzuweisung zum Fallback für die Verschiebezuweisung macht, wenn Verschieben nicht verfügbar ist.
Es ist nicht spezifiziert, ob virtuelle Basisklassen-Subobjekte, die über mehr als einen Pfad im Vererbungsgitter erreichbar sind, mehr als einmal durch den implizit definierten Kopierzuweisungsoperator zugewiesen werden (dasselbe gilt für Move Assignment ).
Siehe assignment operator overloading für weitere Details zum erwarteten Verhalten eines benutzerdefinierten copy-assignment operators.
Beispiel
#include <algorithm> #include <iostream> #include <memory> #include <string> struct A { int n; std::string s1; A() = default; A(A const&) = default; // benutzerdefinierte Kopierzuweisung (Copy-and-Swap Idiom) A& operator=(A other) { std::cout << "copy assignment of A\n"; std::swap(n, other.n); std::swap(s1, other.s1); return *this; } }; struct B : A { std::string s2; // implizit definierte Kopierzuweisung }; struct C { std::unique_ptr<int[]> data; std::size_t size; // benutzerdefinierte Kopierzuweisung (nicht Copy-and-Swap Idiom) // Hinweis: Copy-and-Swap würde immer Ressourcen neu zuweisen C& operator=(const C& other) { if (this != &other) // keine Selbstzuweisung { if (size != other.size) // Ressource kann nicht wiederverwendet werden { data.reset(new int[other.size]); size = other.size; } std::copy(&other.data[0], &other.data[0] + size, &data[0]); } return *this; } }; int main() { A a1, a2; std::cout << "a1 = a2 calls "; a1 = a2; // benutzerdefinierte Kopierzuweisung B b1, b2; b2.s1 = "foo"; b2.s2 = "bar"; std::cout << "b1 = b2 calls "; b1 = b2; // implizit definierte Kopierzuweisung std::cout << "b1.s1 = " << b1.s1 << "; b1.s2 = " << b1.s2 << '\n'; }
Ausgabe:
a1 = a2 calls copy assignment of A b1 = b2 calls copy assignment of A b1.s1 = foo; b1.s2 = bar
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 1353 | C++98 |
die Bedingungen, unter denen implizit deklarierte Kopierzuweisungsoperatoren
undefiniert sind, berücksichtigten keine mehrdimensionalen Array-Typen |
diese Typen berücksichtigen |
| CWG 2094 | C++11 |
ein volatile-Subobjekt machte standardmäßige Kopier-
zuweisungsoperatoren nicht-trivial ( CWG issue 496 ) |
Trivialität nicht betroffen |
| CWG 2171 | C++11 | operator = ( X & ) = default war nicht-trivial | trivial gemacht |
| CWG 2180 | C++11 |
ein standardmäßiger Kopierzuweisungsoperator für Klasse
T
wurde nicht als gelöscht definiert
wenn
T
abstrakt ist und nicht kopierzuweisbare direkte virtuelle Basisklassen hat
|
der Operator wird in diesem Fall
als gelöscht definiert |
| CWG 2595 | C++20 |
ein Kopierzuweisungsoperator war nicht geeignet, wenn es
einen anderen Kopierzuweisungsoperator gibt, der stärker eingeschränkt ist, aber seine zugehörigen Constraints nicht erfüllt |
er kann in diesem Fall
geeignet sein |