Namensräume
Varianten
Aktionen

Kopierzuweisungsoperator

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

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

1) Deklaration eines Kopierzuweisungsoperators innerhalb der Klassendefinition.
2-4) Definition eines Kopierzuweisungsoperators innerhalb der Klassendefinition.
3) Der Kopierzuweisungsoperator ist explizit per Default.
4) Der Kopierzuweisungsoperator ist gelöscht.
5,6) Definition eines Kopierzuweisungsoperators außerhalb der Klassendefinition (die Klasse muss eine Deklaration (1) enthalten).
6) Der Kopierzuweisungsoperator ist explizit per Default.
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 B von T hat einen Kopierzuweisungsoperator, dessen Parameter B oder const B& oder const volatile B& sind;
  • jeder nicht-statische Datenmember M von T vom Klassentyp oder Array von Klassentyp hat einen Kopierzuweisungsoperator, dessen Parameter M oder 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 T ist constexpr, wenn

  • T ein Literal-Typ ist, und
  • der für die Kopie jedes direkten Basisklassen-Subobjekts ausgewählte Zuweisungsoperator eine constexpr-Funktion ist, und
  • für jeden nicht-statischen Datenmember von T, der vom Klassentyp (oder einem Array davon) ist, der für die Kopie dieses Members ausgewählte Zuweisungsoperator eine constexpr-Funktion ist.
(seit C++14)
(bis C++23)

Der implizit definierte Kopierzuweisungsoperator für eine Klasse T ist constexpr.

(seit C++23)


Die Generierung des implizit definierten Kopierzuweisungsoperators ist veraltet, wenn T einen benutzerdeklarierten Destruktor oder einen benutzerdeklareren Kopierkonstruktor hat.

(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:

  • T hat einen nicht-statischen Datenmember eines const-qualifizierten Nicht-Klassentyps (oder möglicherweise eines mehrdimensionalen Arrays davon).
  • T hat einen nicht-statischen Datenmember eines Referenztyps.
  • T hat ein potenziell konstruierbares Subobjekt vom Klassentyp M (oder möglicherweise eines mehrdimensionalen Arrays davon), so dass die Überladungsauflösung zur Ermittlung des Kopierzuweisungsoperators von M
  • 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 T wird als gelöscht definiert, wenn T einen Move-Konstruktor oder Move-Zuweisungsoperator deklariert.

(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);
  • T keine virtuellen Memberfunktionen hat;
  • T keine virtuellen Basisklassen hat;
  • der für jeden direkten Basis von T ausgewählte Kopierzuweisungsoperator ist trivial;
  • der für jedes nicht-statische Klassen-Typ-Mitglied (oder Array von Klassen-Typ-Mitgliedern) von T ausgewä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 definiert
wenn 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

[edit] Siehe auch