Kopierzuweisungsoperator
Ein Kopierzuweisungsoperator ist eine nicht-template nicht-statische Memberfunktion mit dem Namen operator=, die mit einem Argument vom Typ der gleichen Klasse aufgerufen werden kann und den Inhalt des Arguments kopiert, ohne das Argument zu verändern.
Inhalt |
[edit] Syntax
Für die formale Syntax des Kopierzuweisungsoperators siehe Funktionsdeklaration. Die folgende Syntaxliste demonstriert nur einen Teil aller gültigen Syntaxen für Kopierzuweisungsoperatoren.
return-type operator=(parameter-list ); |
(1) | ||||||||
return-type operator=(parameter-list ) function-body |
(2) | ||||||||
return-type operator=(parameter-list-no-default ) = default; |
(3) | (seit C++11) | |||||||
return-type operator=(parameter-list ) = delete; |
(4) | (seit C++11) | |||||||
return-type class-name ::operator=(parameter-list ) function-body |
(5) | ||||||||
return-type class-name ::operator=(parameter-list-no-default ) = default; |
(6) | (seit C++11) | |||||||
| Klassenname | - | die Klasse, deren Kopierzuweisungsoperator deklariert wird; der Klassentyp wird in den folgenden Beschreibungen als T angegeben |
| parameter-liste | - | eine Parameterliste mit nur einem Parameter vom Typ T, T&, const T&, volatile T& oder const volatile T& |
| parameter-list-no-default | - | eine Parameterliste mit nur einem Parameter vom Typ T, T&, const T&, volatile T& oder const volatile T& und ohne Standardargument |
| function-body | - | der Funktionsrumpf des Kopierzuweisungsoperators |
| return-type | - | jeder Typ, aber T& wird bevorzugt, um Zuweisungsketten zu ermöglichen |
[edit] Erklärung
struct X { X& operator=(X& other); // copy assignment operator X operator=(X other); // pass-by-value is allowed // X operator=(const X other); // Error: incorrect parameter type }; union Y { // copy assignment operators can have syntaxes not listed above, // as long as they follow the general function declaration syntax // and do not viloate the restrictions listed above auto operator=(Y& other) -> Y&; // OK: trailing return type Y& operator=(this Y& self, Y& other); // OK: explicit object parameter // Y& operator=(Y&, int num = 1); // Error: has other non-object parameters };
Der Kopierzuweisungsoperator wird immer dann aufgerufen, wenn er durch Überladungsauflösung ausgewählt wird, z. B. wenn ein Objekt auf der linken Seite eines Zuweisungsausdrucks erscheint.
[edit] Implizit deklarierter Kopierzuweisungsoperator
Wenn für einen Klassentyp keine benutzerdefinierten Kopierzuweisungsoperatoren bereitgestellt werden, deklariert der Compiler immer einen als inline public Member der Klasse. Dieser implizit deklarierte Kopierzuweisungsoperator hat die Form T& T::operator=(const T&), wenn alle der folgenden Bedingungen erfüllt sind:
- jeder direkte Basis
BvonThat einen Kopierzuweisungsoperator, dessen ParameterBoder const B& oder const volatile B& sind; - jeder nicht-statische Datenmember
MvonTvom Klassentyp oder Array von Klassentyp hat einen Kopierzuweisungsoperator, dessen ParameterModer const M& oder const volatile M& sind.
Andernfalls wird der implizit deklarierte Kopierzuweisungsoperator als T& T::operator=(T&) deklariert.
Aufgrund dieser Regeln kann der implizit deklarierte Kopierzuweisungsoperator nicht an ein volatile Lvalue-Argument gebunden werden.
Eine Klasse kann mehrere Kopierzuweisungsoperatoren haben, z. B. sowohl T& T::operator=(T&) als auch T& T::operator=(T). Wenn einige benutzerdefinierte Kopierzuweisungsoperatoren vorhanden sind, kann der Benutzer die Generierung des implizit deklarierten Kopierzuweisungsoperators immer noch mit dem Schlüsselwort default erzwingen.(seit C++11)
Der implizit deklarierte (oder bei seiner ersten Deklaration standardmäßig gesetzte) Kopierzuweisungsoperator hat eine Ausnahmespezifikation, wie in dynamischer Ausnahmespezifikation(bis C++17)noexcept-Spezifikation(seit C++17) beschrieben.
Da der Kopierzuweisungsoperator für jede Klasse immer deklariert wird, ist der Basisklassen-Zuweisungsoperator immer versteckt. Wenn eine using-Deklaration verwendet wird, um den Zuweisungsoperator von der Basisklasse zu importieren, und sein Argumenttyp derselbe sein könnte wie der Argumenttyp des impliziten Zuweisungsoperators der abgeleiteten Klasse, wird die using-Deklaration ebenfalls durch die implizite Deklaration versteckt.
[edit] 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, wenn er ODR-used ist oder für die Konstantenbewertung benötigt wird(seit C++14). Für Union-Typen kopiert der implizit definierte Kopierzuweisungsoperator die Objektrepräsentation (wie mit std::memmove). Für Nicht-Union-Klassentypen führt der Operator eine elementweise Kopierzuweisung der direkten Basen und nicht-statischen Datenmember des Objekts in ihrer Initialisierungsreihenfolge durch, wobei eingebaute Zuweisung für Skalare, elementweise Kopierzuweisung für Arrays und Kopierzuweisungsoperator für Klassentypen (nicht-virtuell aufgerufen) verwendet werden.
|
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 Generierung des implizit definierten Kopierzuweisungsoperators ist veraltet, wenn |
(seit C++11) |
[edit] Gelöschter Kopierzuweisungsoperator
Ein implizit deklarierter oder explizit per Default gesetzter(seit C++11) Kopierzuweisungsoperator für die Klasse T ist undefiniert(bis C++11)definiert als gelöscht(seit C++11), wenn eine der folgenden Bedingungen erfüllt ist:
-
That einen nicht-statischen Datenmember eines const-qualifizierten Nicht-Klassentyps (oder möglicherweise eines mehrdimensionalen Arrays davon). -
That einen nicht-statischen Datenmember eines Referenztyps. -
That ein potenziell konstruierbares Subobjekt vom KlassentypM(oder möglicherweise eines mehrdimensionalen Arrays davon), so dass die Überladungsauflösung zur Ermittlung des Kopierzuweisungsoperators vonM
- nicht zu einem benutzbaren Kandidaten führt, oder
- im Falle des Subobjekts als Varianten-Mitglied eine nicht-triviale Funktion auswählt.
|
Der implizit deklarierte Kopierzuweisungsoperator für die Klasse |
(seit C++11) |
[edit] Trivialer Kopierzuweisungsoperator
Der Kopierzuweisungsoperator für die Klasse T ist trivial, wenn alle der folgenden Bedingungen erfüllt sind:
- er ist nicht benutzerdefiniert (d. h., er ist implizit definiert oder per Default gesetzt);
-
Tkeine virtuellen Memberfunktionen hat; -
Tkeine virtuellen Basisklassen hat; - der für jeden direkten Basis von
Tausgewählte Kopierzuweisungsoperator ist trivial; - der für jedes nicht-statische Klassen-Typ-Mitglied (oder Array von Klassen-Typ-Mitgliedern) von
Tausgewählte Kopierzuweisungsoperator ist trivial.
Ein trivialer Kopierzuweisungsoperator kopiert die Objektrepräsentation, als ob durch std::memmove. Alle mit der C-Sprache kompatiblen Datentypen (POD-Typen) sind trivial kopierzuweisbar.
[edit] Gültiger Kopierzuweisungsoperator
|
Ein Kopierzuweisungsoperator ist gültig, wenn er entweder benutzerdeklariert oder sowohl implizit deklariert als auch definierbar ist. |
(bis C++11) |
|
Ein Kopierzuweisungsoperator ist gültig, wenn er nicht gelöscht ist. |
(seit C++11) (bis C++20) |
|
Ein Kopierzuweisungsoperator ist gültig, wenn alle folgenden Bedingungen erfüllt sind:
|
(seit C++20) |
Die Trivialität gültiger Kopierzuweisungsoperatoren bestimmt, ob die Klasse ein trivially copyable Typ ist.
[edit] Hinweise
Wenn sowohl Kopier- als auch Move-Zuweisungsoperatoren bereitgestellt werden, wählt die Überladungsauflösung den Move-Zuweisungsoperator aus, wenn das Argument ein Rvalue ist (entweder ein prvalue wie ein namenloses temporäres Objekt oder ein xvalue wie das Ergebnis von std::move), und wählt den Kopierzuweisungsoperator aus, wenn das Argument ein Lvalue ist (benanntes Objekt oder eine Funktion/ein Operator, der eine Lvalue-Referenz zurückgibt). Wenn nur der Kopierzuweisungsoperator bereitgestellt wird, wählen alle Argumentkategorien ihn aus (solange er sein Argument per Wert oder als Referenz auf const annimmt, da Rvalues an const-Referenzen gebunden werden können), was den Kopierzuweisungsoperator zum Fallback für den Move-Zuweisungsoperator macht, wenn Move nicht verfügbar ist.
Es ist nicht spezifiziert, ob virtuelle Basisklassen-Subobjekte, die über mehr als einen Pfad im Vererbungsgitter zugänglich sind, durch den implizit definierten Kopierzuweisungsoperator mehr als einmal zugewiesen werden (gleiches gilt für Move-Zuweisung).
Weitere Details zum erwarteten Verhalten eines benutzerdefinierten Kopier-Zuweisungsoperators finden Sie unter Zuweisungsoperator-Überladung.
[edit] Beispiel
#include <algorithm> #include <iostream> #include <memory> #include <string> struct A { int n; std::string s1; A() = default; A(A const&) = default; // user-defined copy assignment (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; // implicitly-defined copy assignment }; struct C { std::unique_ptr<int[]> data; std::size_t size; // user-defined copy assignment (non copy-and-swap idiom) // note: copy-and-swap would always reallocate resources C& operator=(const C& other) { if (this != &other) // not a self-assignment { if (size != other.size) // resource cannot be reused { 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; // user-defined copy assignment B b1, b2; b2.s1 = "foo"; b2.s2 = "bar"; std::cout << "b1 = b2 calls "; b1 = b2; // implicitly-defined copy assignment 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
[edit] 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 waren, berücksichtigten keine mehrdimensionalen Array-Typen |
diese Typen berücksichtigen |
| CWG 2094 | C++11 | ein volatile Subobjekt, das standardmäßig kopiert wurde Zuweisungsoperatoren nicht-trivial (CWG issue 496) |
Trivialität nicht betroffen |
| CWG 2171 | C++11 | operator=(X&) = default war nicht trivial | wurde trivial |
| CWG 2180 | C++11 | ein standardmäßig gesetzter Kopierzuweisungsoperator für die Klasse T wurde nicht als gelöscht definiertwenn T abstrakt ist und nicht kopierzuweisbare direkte virtuelle Basisklassen hat |
der Operator ist definiert in diesem Fall als gelöscht |
| CWG 2595 | C++20 | ein Kopierzuweisungsoperator war nicht gültig, wenn es dort einen anderen Kopierzuweisungsoperator gibt, der stärker eingeschränkt ist, aber seine zugehörigen Einschränkungen nicht erfüllt |
er kann gültig sein in diesem Fall |