Namespaces
Variants

operator overloading

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Passt die C++-Operatoren für Operanden benutzerdefinierter Typen an.

Inhaltsverzeichnis

Syntax

Operatorfunktionen sind Funktionen mit speziellen Funktionsnamen:

operator op (1)
operator new
operator new []
(2)
operator delete
operator delete []
(3)
operator co_await (4) (seit C++20)
op - einer der folgenden Operatoren: + - * / % ^ & | ~ ! = < > + = - = * = / = % = ^ = & = | = << >> >>= <<= == ! = <= >= <=> (seit C++20) && || ++ -- , - > * - > ( ) [ ]
1) Ein überladener Interpunktionsoperator.
4) Ein überladener co_await Operator zur Verwendung in co_await Ausdrücken .

Das Verhalten von Nicht-Interpunktionsoperatoren wird auf ihren jeweiligen eigenen Seiten beschrieben. Sofern nicht anders angegeben, gilt die verbleibende Beschreibung auf dieser Seite nicht für diese Funktionen.

Erklärung

Wenn ein Operator in einem Ausdruck auftritt und mindestens einer seiner Operanden einen Klassentyp oder einen Aufzählungstyp hat, dann wird Überladungsauflösung verwendet, um die benutzerdefinierte Funktion zu bestimmen, die unter allen Funktionen aufgerufen werden soll, deren Signaturen mit Folgendem übereinstimmen:

Ausdruck Als Member-Funktion Als Nicht-Member-Funktion Beispiel
@a (a).operator@ ( ) operator@ (a) ! std:: cin ruft auf std:: cin . operator ! ( )
a@b (a).operator@ (b) operator@ (a, b) std:: cout << 42 ruft auf std:: cout . operator << ( 42 )
a=b (a).operator= (b) kann nicht als Nicht-Member definiert werden Gegeben std:: string s ; , s = "abc" ; ruft auf s. operator = ( "abc" )
a(b...) (a).operator()(b...) kann nicht als Nicht-Member definiert werden Gegeben std:: random_device r ; , auto n = r ( ) ; ruft auf r. operator ( ) ( )
a[b...] (a).operator[](b...) kann nicht als Nicht-Member definiert werden Gegeben std:: map < int , int > m ; , m [ 1 ] = 2 ; ruft auf m. operator [ ] ( 1 )
a-> (a).operator->( ) kann nicht als Nicht-Member definiert werden Gegeben std:: unique_ptr < S > p ; , p - > bar ( ) ruft auf p. operator - > ( )
a@ (a).operator@ (0) operator@ (a, 0) Gegeben std:: vector < int > :: iterator i ; , i ++ ruft auf i. operator ++ ( 0 )

In dieser Tabelle ist @ ein Platzhalter für alle passenden Operatoren: alle Präfix-Operatoren in @a, alle Postfix-Operatoren außer -> in a@, alle Infix-Operatoren außer = in a@b.

Zusätzlich berücksichtigt die Überladungsauflösung für Vergleichsoperatoren == , ! = , < , > , <= , >= , <=> auch die umgeschriebenen Kandidaten operator == oder operator <=> .

(seit C++20)

Überladene Operatoren (aber nicht die eingebauten Operatoren) können mit Funktionsschreibweise aufgerufen werden:

std::string str = "Hallo, ";
str.operator+=("Welt");                      // dasselbe wie str += "Welt";
operator<<(operator<<(std::cout, str), '\n'); // dasselbe wie std::cout << str << '\n';
                                              // (seit C++17) außer in der Ausführungsreihenfolge

Statische überladene Operatoren

Überladene Operatoren, die Elementfunktionen sind, können als static deklariert werden. Dies ist jedoch nur für operator ( ) und operator [ ] erlaubt.

Solche Operatoren können mit Funktionsnotation aufgerufen werden. Wenn diese Operatoren jedoch in Ausdrücken erscheinen, benötigen sie weiterhin ein Objekt vom Klassentyp.

struct SwapThem
{
    template<typename T>
    static void operator()(T& lhs, T& rhs) 
    {
        std::ranges::swap(lhs, rhs);
    }
    template<typename T>
    static void operator[](T& lhs, T& rhs)
    {
        std::ranges::swap(lhs, rhs);
    } 
};
inline constexpr SwapThem swap_them{};
void foo()
{
    int a = 1, b = 2;
    swap_them(a, b); // OK
    swap_them[a, b]; // OK
    SwapThem{}(a, b); // OK
    SwapThem{}[a, b]; // OK
    SwapThem::operator()(a, b); // OK
    SwapThem::operator[](a, b); // OK
    SwapThem(a, b); // error, invalid construction
    SwapThem[a, b]; // error
}
(seit C++23)

Einschränkungen

  • Eine Operatorfunktion muss mindestens einen Funktionsparameter oder impliziten Objektparameter haben, dessen Typ eine Klasse, eine Referenz auf eine Klasse, eine Enumeration oder eine Referenz auf eine Enumeration ist.
  • Die Operatoren :: (Bereichsauflösung), . (Memberzugriff), .* (Memberzugriff über Zeiger auf Member) und ?: (ternärer Bedingungsoperator) können nicht überladen werden.
  • Neue Operatoren wie ** , <> oder &| können nicht erstellt werden.
  • Es ist nicht möglich, die Priorität, Gruppierung oder Anzahl der Operanden von Operatoren zu ändern.
  • Die Überladung des Operators -> muss entweder einen Rohzeiger zurückgeben oder ein Objekt (per Referenz oder per Wert) zurückgeben, für das der Operator -> seinerseits überladen ist.
  • Die Überladungen der Operatoren && und || verlieren die Kurzschlussauswertung.
  • && , || , und , verlieren ihre speziellen Sequenzierungseigenschaften , wenn sie überladen werden, und verhalten sich wie reguläre Funktionsaufrufe, selbst wenn sie ohne Funktionsaufrufnotation verwendet werden.
(bis C++17)

Kanonische Implementierungen

Neben den oben genannten Einschränkungen legt die Sprache keine weiteren Beschränkungen fest, was die überladenen Operatoren tun oder welchen Rückgabetyp sie haben (er nimmt nicht an der Überladungsauflösung teil), aber im Allgemeinen wird erwartet, dass überladene Operatoren sich möglichst ähnlich wie die eingebauten Operatoren verhalten: operator + wird erwartet zu addieren, anstatt seine Argumente zu multiplizieren, operator = wird erwartet zuzuweisen, usw. Die verwandten Operatoren werden erwartet, sich ähnlich zu verhalten ( operator + und operator + = führen die gleiche additionsähnliche Operation durch). Die Rückgabetypen werden durch die Ausdrücke begrenzt, in denen der Operator verwendet werden soll: Zum Beispiel geben Zuweisungsoperatoren per Referenz zurück, um das Schreiben von a = b = c = d zu ermöglichen, weil die eingebauten Operatoren dies erlauben.

Häufig überladene Operatoren haben folgende typische, kanonische Formen: [1]

Zuweisungsoperator

Der Zuweisungsoperator operator = besitzt spezielle Eigenschaften: siehe copy assignment und move assignment für Details.

Der kanonische Kopierzuweisungsoperator wird erwartet, sicher bei Selbstzuweisung zu sein und die linke Seite als Referenz zurückzugeben:

// Kopierzuweisung
T& operator=(const T& other)
{
    // Selbstzuweisung prüfen
    if (this == &other)
        return *this;
    // annehmen *this verwaltet eine wiederverwendbare Ressource, wie einen heap-allokierten Puffer mArray
    if (size != other.size)           // Ressource in *this kann nicht wiederverwendet werden
    {
        temp = new int[other.size];   // Ressource allokieren, falls Exception, nichts tun
        delete[] mArray;              // Ressource in *this freigeben
        mArray = temp;
        size = other.size;
    }
    std::copy(other.mArray, other.mArray + other.size, mArray);
    return *this;
}

Die kanonische Move-Zuweisung wird erwartet, das verschobene Objekt in einem gültigen Zustand zu belassen (d.h. in einem Zustand mit intakten Klasseninvarianten), und entweder nichts zu tun oder zumindest das Objekt bei Selbstzuweisung in einem gültigen Zustand zu belassen, und die linke Seite als Referenz auf Nicht-Konstantes zurückzugeben, und noexcept zu sein:

// move assignment
T& operator=(T&& other) noexcept
{
    // Guard self assignment
    if (this == &other)
        return *this; // delete[]/size=0 would also be ok
    delete[] mArray;                               // release resource in *this
    mArray = std::exchange(other.mArray, nullptr); // leave other in valid state
    size = std::exchange(other.size, 0);
    return *this;
}
(seit C++11)

In jenen Situationen, in denen die Kopierzuweisung nicht von der Wiederverwendung von Ressourcen profitieren kann (sie verwaltet kein heap-alloziertes Array und hat kein (möglicherweise transitives) Member, das dies tut, wie etwa ein Member std::vector oder std::string ), gibt es eine beliebte praktische Abkürzung: den Copy-and-Swap-Zuweisungsoperator, der seinen Parameter als Wert übernimmt (somit sowohl als Kopier- als auch als Verschiebezuweisung funktioniert, abhängig von der Wertkategorie des Arguments), mit dem Parameter swap ausführt und den Destruktor die Bereinigung überlässt.

// Kopierzuweisung (Copy-and-Swap Idiom)
T& T::operator=(T other) noexcept // ruft Kopier- oder Move-Konstruktor auf, um other zu konstruieren
{
    std::swap(size, other.size); // tauscht Ressourcen zwischen *this und other aus
    std::swap(mArray, other.mArray);
    return *this;
} // Destruktor von other wird aufgerufen, um die zuvor von *this verwalteten Ressourcen freizugeben

Dieses Formular bietet automatisch strong exception guarantee , verbietet jedoch die Wiederverwendung von Ressourcen.

Stream-Extraktion und -Einfügung

Die Überladungen von operator>> und operator<< , die ein std:: istream & oder std:: ostream & als linkes Argument nehmen, werden als Einfüge- und Extraktionsoperatoren bezeichnet. Da sie den benutzerdefinierten Typ als rechtes Argument nehmen ( b in a @ b ), müssen sie als Nicht-Member-Funktionen implementiert werden.

std::ostream& operator<<(std::ostream& os, const T& obj)
{
    // Objekt in Stream schreiben
    return os;
}
std::istream& operator>>(std::istream& is, T& obj)
{
    // Objekt aus Stream lesen
    if (/* T konnte nicht konstruiert werden */)
        is.setstate(std::ios::failbit);
    return is;
}

Diese Operatoren werden manchmal als friend functions implementiert.

Funktionsaufrufoperator

Wenn eine benutzerdefinierte Klasse den Funktionsaufrufoperator operator ( ) überlädt, wird sie zu einem FunctionObject -Typ.

Ein Objekt eines solchen Typs kann in einem Funktionsaufrufausdruck verwendet werden:

// Ein Objekt dieses Typs repräsentiert eine lineare Funktion einer Variablen a * x + b.
struct Linear
{
    double a, b;
    double operator()(double x) const
    {
        return a * x + b;
    }
};
int main()
{
    Linear f{2, 1};  // Repräsentiert die Funktion 2x + 1.
    Linear g{-1, 0}; // Repräsentiert die Funktion -x.
    // f und g sind Objekte, die wie eine Funktion verwendet werden können.
    double f_0 = f(0);
    double f_1 = f(1);
    double g_0 = g(0);
}

Viele Standardbibliothek- Algorithmen akzeptieren FunctionObject s zur Anpassung des Verhaltens. Es gibt keine besonders bemerkenswerten kanonischen Formen von operator ( ) , aber zur Veranschaulichung der Verwendung:

#include <algorithm>
#include <iostream>
#include <vector>
struct Sum
{
    int sum = 0;
    void operator()(int n) { sum += n; }
};
int main()
{
    std::vector<int> v = {1, 2, 3, 4, 5};
    Sum s = std::for_each(v.begin(), v.end(), Sum());
    std::cout << "The sum is " << s.sum << '\n';
}

Ausgabe:

The sum is 15

Inkrement und Dekrement

Wenn der Postfix-Inkrement- oder -Dekrement-Operator in einem Ausdruck erscheint, wird die entsprechende benutzerdefinierte Funktion ( operator ++ oder operator -- ) mit einem ganzzahligen Argument 0 aufgerufen. Typischerweise wird sie deklariert als T operator ++ ( int ) oder T operator -- ( int ) , wobei das Argument ignoriert wird. Die Postfix-Inkrement- und -Dekrement-Operatoren werden üblicherweise mithilfe der Präfix-Versionen implementiert:

struct X
{
    // Präfix-Inkrement
    X& operator++()
    {
        // tatsächliche Erhöhung findet hier statt
        return *this; // neuen Wert als Referenz zurückgeben
    }
    // Postfix-Inkrement
    X operator++(int)
    {
        X old = *this; // alten Wert kopieren
        operator++();  // Präfix-Inkrement
        return old;    // alten Wert zurückgeben
    }
    // Präfix-Dekrement
    X& operator--()
    {
        // tatsächliche Verringerung findet hier statt
        return *this; // neuen Wert als Referenz zurückgeben
    }
    // Postfix-Dekrement
    X operator--(int)
    {
        X old = *this; // alten Wert kopieren
        operator--();  // Präfix-Dekrement
        return old;    // alten Wert zurückgeben
    }
};

Obwohl die kanonischen Implementierungen der Präfix-Inkrement- und Dekrement-Operatoren per Referenz zurückgeben, ist der Rückgabetyp – wie bei jeder Operatorüberladung – benutzerdefiniert; beispielsweise geben die Überladungen dieser Operatoren für std::atomic per Wert zurück.

Binäre arithmetische Operatoren

Binäre Operatoren werden typischerweise als Nicht-Member implementiert, um Symmetrie zu wahren (wenn man beispielsweise eine komplexe Zahl und eine Ganzzahl addiert, würde nur operator + als Member-Funktion des komplexen Typs kompilieren, und nicht integer + complex ). Da es für jeden binären arithmetischen Operator einen entsprechenden zusammengesetzten Zuweisungsoperator gibt, werden kanonische Formen binärer Operatoren mittels ihrer zusammengesetzten Zuweisungen implementiert:

class X
{
public:
    X& operator+=(const X& rhs) // zusammengesetzte Zuweisung (muss kein Member sein,
    {                           // ist es aber oft, um private Member zu modifizieren)
        /* addition of rhs to *this takes place here */
        return *this; // Ergebnis als Referenz zurückgeben
    }
    // im Klassenkörper definierte friend-Funktionen sind inline und vor nicht-ADL-Lookup verborgen
    friend X operator+(X lhs,        // Übergabe von lhs als Wert optimiert verkettete a+b+c
                       const X& rhs) // andernfalls können beide Parameter const-Referenzen sein
    {
        lhs += rhs; // zusammengesetzte Zuweisung wiederverwenden
        return lhs; // Ergebnis als Wert zurückgeben (verwendet Move-Konstruktor)
    }
};

Vergleichsoperatoren

Standardbibliotheksalgorithmen wie std::sort und Container wie std::set erwarten standardmäßig, dass operator < für benutzerdefinierte Typen definiert ist, und erwarten, dass dieser eine strenge schwache Ordnung implementiert (und somit die Compare -Anforderungen erfüllt). Eine idiomatische Methode zur Implementierung einer strengen schwachen Ordnung für eine Struktur ist die Verwendung des lexikographischen Vergleichs, der durch std::tie bereitgestellt wird:

struct Record
{
    std::string name;
    unsigned int floor;
    double weight;
    friend bool operator<(const Record& l, const Record& r)
    {
        return std::tie(l.name, l.floor, l.weight)
             < std::tie(r.name, r.floor, r.weight); // Reihenfolge beibehalten
    }
};

In der Regel, sobald operator < bereitgestellt wird, werden die anderen relationalen Operatoren mittels operator < implementiert.

inline bool operator< (const X& lhs, const X& rhs) { /* tatsächlichen Vergleich durchführen */ }
inline bool operator> (const X& lhs, const X& rhs) { return rhs < lhs; }
inline bool operator<=(const X& lhs, const X& rhs) { return !(lhs > rhs); }
inline bool operator>=(const X& lhs, const X& rhs) { return !(lhs < rhs); }

Ebenso wird der Ungleichheitsoperator typischerweise in Bezug auf operator == implementiert:

inline bool operator==(const X& lhs, const X& rhs) { /* tatsächlichen Vergleich durchführen */ }
inline bool operator!=(const X& lhs, const X& rhs) { return !(lhs == rhs); }

Wenn ein Dreivergleich (wie std::memcmp oder std::string::compare ) bereitgestellt wird, können alle sechs zweistelligen Vergleichsoperatoren dadurch ausgedrückt werden:

inline bool operator==(const X& lhs, const X& rhs) { return cmp(lhs,rhs) == 0; }
inline bool operator!=(const X& lhs, const X& rhs) { return cmp(lhs,rhs) != 0; }
inline bool operator< (const X& lhs, const X& rhs) { return cmp(lhs,rhs) <  0; }
inline bool operator> (const X& lhs, const X& rhs) { return cmp(lhs,rhs) >  0; }
inline bool operator<=(const X& lhs, const X& rhs) { return cmp(lhs,rhs) <= 0; }
inline bool operator>=(const X& lhs, const X& rhs) { return cmp(lhs,rhs) >= 0; }
**Anmerkung:** Der bereitgestellte Code enthält ausschließlich C++-Code innerhalb von `
`-Tags, der gemäß den Anweisungen nicht übersetzt werden soll. Daher bleibt der gesamte Inhalt unverändert.

Array-Subscript-Operator

Benutzerdefinierte Klassen, die array-ähnlichen Zugriff mit Lese- und Schreibmöglichkeiten bereitstellen, definieren typischerweise zwei Überladungen für operator [ ] : const- und non-const-Varianten:

struct T
{
          value_t& operator[](std::size_t idx)       { return mVector[idx]; }
    const value_t& operator[](std::size_t idx) const { return mVector[idx]; }
};
**Anmerkung:** Der bereitgestellte Code enthält keine übersetzbaren Textelemente außerhalb der HTML-Tags und Code-Blöcke. Es handelt sich ausschließlich um C++-Code, der gemäß den Anweisungen nicht übersetzt werden soll. Die HTML-Struktur und Formatierung wurden unverändert beibehalten.

Alternativ können sie als einzelne Member-Funktionsvorlage mit einem expliziten Objektparameter ausgedrückt werden:

struct T
{
    decltype(auto) operator[](this auto& self, std::size_t idx) 
    { 
        return self.mVector[idx]; 
    }
};
(seit C++23)

Wenn der Werttyp bekanntermaßen ein Skalartyp ist, sollte die const-Variante als Wert zurückgeben.

Wenn direkter Zugriff auf die Elemente des Containers nicht erwünscht oder nicht möglich ist oder zwischen lvalue c [ i ] = v ; und rvalue v = c [ i ] ; Verwendung unterschieden werden muss, operator [ ] kann einen Proxy zurückgeben. Siehe beispielsweise std::bitset::operator[] .

operator [ ] kann nur einen Index akzeptieren. Um mehrdimensionale Array-Zugriffssemantik bereitzustellen, z.B. um einen 3D-Array-Zugriff zu implementieren a [ i ] [ j ] [ k ] = x ; , muss operator [ ] eine Referenz auf eine 2D-Ebene zurückgeben, die ihren eigenen operator [ ] haben muss, der eine Referenz auf eine 1D-Zeile zurückgibt, die wiederum operator [ ] haben muss, der eine Referenz auf das Element zurückgibt. Um diese Komplexität zu vermeiden, überladen einige Bibliotheken stattdessen operator ( ) , sodass 3D-Zugriffsausdrücke die Fortran-ähnliche Syntax a ( i, j, k ) = x ; haben.

(bis C++23)

operator [ ] kann beliebig viele Indizes akzeptieren. Beispielsweise kann ein operator [ ] einer 3D-Array-Klasse, deklariert als T & operator [ ] ( std:: size_t x, std:: size_t y, std:: size_t z ) ; , direkt auf die Elemente zugreifen.

#include <array>
#include <cassert>
#include <iostream>
template<typename T, std::size_t Z, std::size_t Y, std::size_t X>
struct Array3d
{
    std::array<T, X * Y * Z> m{};
    constexpr T& operator[](std::size_t z, std::size_t y, std::size_t x) // C++23
    {
        assert(x < X and y < Y and z < Z);
        return m[z * Y * X + y * X + x];
    }
};
int main()
{
    Array3d<int, 4, 3, 2> v;
    v[3, 2, 1] = 42;
    std::cout << "v[3, 2, 1] = " << v[3, 2, 1] << '\n';
}

Ausgabe:

v[3, 2, 1] = 42
(seit C++23)

Bitweise arithmetische Operatoren

Benutzerdefinierte Klassen und Enumerationen, die die Anforderungen von BitmaskType implementieren, müssen die bitweisen arithmetischen Operatoren operator & , operator | , operator ^ , operator~ , operator & = , operator | = , und operator ^ = überladen, und können optional die Schiebeoperatoren operator << operator >> , operator >>= , und operator <<= überladen. Die kanonischen Implementierungen folgen üblicherweise dem oben beschriebenen Muster für binäre arithmetische Operatoren.

Boolescher Negationsoperator

Der Operator operator ! wird häufig von benutzerdefinierten Klassen überladen, die in booleschen Kontexten verwendet werden sollen. Solche Klassen stellen auch eine benutzerdefinierte Konvertierungsfunktion zum booleschen Typ bereit (siehe std::basic_ios für das Standardbibliotheksbeispiel), und das erwartete Verhalten von operator ! ist, den umgekehrten Wert von operator bool zurückzugeben.

(bis C++11)

Da der eingebaute Operator ! eine kontextuelle Konvertierung zu bool durchführt, können benutzerdefinierte Klassen, die in booleschen Kontexten verwendet werden sollen, nur operator bool bereitstellen und müssen operator ! nicht überladen.

(seit C++11)

Selten überladene Operatoren

Die folgenden Operatoren werden selten überladen:

  • Der Address-of-Operator, operator & . Wenn der unäre & auf einen L-Wert eines unvollständigen Typs angewendet wird und der vollständige Typ einen überladenen operator & deklariert, ist es unspezifiziert, ob der Operator die eingebaute Bedeutung hat oder die Operatorfunktion aufgerufen wird. Da dieser Operator überladen werden kann, verwenden generische Bibliotheken std::addressof , um Adressen von Objekten benutzerdefinierter Typen zu erhalten. Das bekannteste Beispiel eines kanonisch überladenen operator & ist die Microsoft-Klasse CComPtrBase . Ein Beispiel für die Verwendung dieses Operators in EDSL findet sich in boost.spirit .
  • Die booleschen Logikoperatoren, operator && und operator || . Im Gegensatz zu den eingebauten Versionen können die Überladungen keine Kurzschlussauswertung implementieren. Anders als bei den eingebauten Versionen sequenzieren sie ihren linken Operanden nicht vor dem rechten. (bis C++17) In der Standardbibliothek sind diese Operatoren nur für std::valarray überladen.
  • Der Komma-Operator, operator, . Im Gegensatz zur eingebauten Version sequenzieren die Überladungen ihren linken Operanden nicht vor dem rechten. (bis C++17) Da dieser Operator überladen werden kann, verwenden generische Bibliotheken Ausdrücke wie a, void ( ) , b anstelle von a, b , um die Ausführung von Ausdrücken benutzerdefinierter Typen zu sequenzieren. Die Boost-Bibliothek verwendet operator, in boost.assign , boost.spirit und anderen Bibliotheken. Die Datenbankzugriffsbibliothek SOCI überlädt ebenfalls operator, .
  • Der Memberzugriff durch Pointer-to-Member operator - > * . Es gibt keine spezifischen Nachteile beim Überladen dieses Operators, aber er wird in der Praxis selten verwendet. Es wurde vorgeschlagen, dass er Teil einer Smart-Pointer-Schnittstelle sein könnte, und tatsächlich wird er in dieser Funktion von Aktoren in boost.phoenix verwendet. Er ist häufiger in EDSLs wie cpp.react zu finden.

Hinweise

Feature-Test Makro Wert Std Funktion
__cpp_static_call_operator 202207L (C++23) static operator ( )
__cpp_multidimensional_subscript 202211L (C++23) static operator [ ]

Schlüsselwörter

operator

Beispiel

#include <iostream>
class Fraction
{
    // or C++17's std::gcd
    constexpr int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
    int n, d;
public:
    constexpr Fraction(int n, int d = 1) : n(n / gcd(n, d)), d(d / gcd(n, d)) {}
    constexpr int num() const { return n; }
    constexpr int den() const { return d; }
    constexpr Fraction& operator*=(const Fraction& rhs)
    {
        int new_n = n * rhs.n / gcd(n * rhs.n, d * rhs.d);
        d = d * rhs.d / gcd(n * rhs.n, d * rhs.d);
        n = new_n;
        return *this;
    }
};
std::ostream& operator<<(std::ostream& out, const Fraction& f)
{
   return out << f.num() << '/' << f.den();
}
constexpr bool operator==(const Fraction& lhs, const Fraction& rhs)
{
    return lhs.num() == rhs.num() && lhs.den() == rhs.den();
}
constexpr bool operator!=(const Fraction& lhs, const Fraction& rhs)
{
    return !(lhs == rhs);
}
constexpr Fraction operator*(Fraction lhs, const Fraction& rhs)
{
    return lhs *= rhs;
}
int main()
{
    constexpr Fraction f1{3, 8}, f2{1, 2}, f3{10, 2};
    std::cout << f1 << " * " << f2 << " = " << f1 * f2 << '\n'
              << f2 << " * " << f3 << " = " << f2 * f3 << '\n'
              <<  2 << " * " << f1 << " = " <<  2 * f1 << '\n';
    static_assert(f3 == f2 * 10);
}

Ausgabe:

3/8 * 1/2 = 3/16
1/2 * 5/1 = 5/2
2 * 3/8 = 3/4

Fehlerberichte

Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR Angewendet auf Verhalten wie veröffentlicht Korrektes Verhalten
CWG 1481 C++98 der nicht-Member Präfix-Inkrementoperator konnte nur einen Parameter
vom Klassentyp, Aufzählungstyp oder Referenztyp auf solche Typen haben
keine Typanforderung
CWG 2931 C++23 explizite Objekt-Member-Operatorfunktionen konnten nur keinen Parameter
vom Klassentyp, Aufzählungstyp oder Referenztyp auf solche Typen haben
verboten

Siehe auch

Häufig verwendete Operatoren
Zuweisung Inkrement
Dekrement
Arithmetik Logisch Vergleich Member
Zugriff
Sonstige

a = b
a + = b
a - = b
a * = b
a / = b
a % = b
a & = b
a | = b
a ^ = b
a <<= b
a >>= b

++ a
-- a
a ++
a --

+ a
- a
a + b
a - b
a * b
a / b
a % b
~a
a & b
a | b
a ^ b
a << b
a >> b

! a
a && b
a || b

a == b
a ! = b
a < b
a > b
a <= b
a >= b
a <=> b

a [ ... ]
* a
& a
a - > b
a. b
a - > * b
a. * b

Funktionsaufruf

a ( ... )
Komma

a, b
Bedingungsoperator

a ? b : c
Spezielle Operatoren

static_cast konvertiert einen Typ in einen anderen verwandten Typ
dynamic_cast konvertiert innerhalb von Vererbungshierarchien
const_cast fügt cv -Qualifizierer hinzu oder entfernt sie
reinterpret_cast konvertiert Typ zu einem unverwandten Typ
C-style cast konvertiert einen Typ zu einem anderen durch eine Kombination von static_cast , const_cast und reinterpret_cast
new erstellt Objekte mit dynamischer Speicherdauer
delete zerstört zuvor durch den new-Ausdruck erstellte Objekte und gibt den belegten Speicherbereich frei
sizeof fragt die Größe eines Typs ab
sizeof... fragt die Größe eines pack ab (since C++11)
typeid fragt die Typinformationen eines Typs ab
noexcept prüft, ob ein Ausdruck eine Exception werfen kann (since C++11)
alignof fragt die Ausrichtungsanforderungen eines Typs ab (since C++11)

Externe Links

  1. Operator Overloading im StackOverflow C++ FAQ