it-swarm.com.de

Warum können überladene Operatoren nicht als statische Member einer Klasse definiert werden?

Mit der C++ - Syntax können überladene Operatoren definiert werden, die sich innerhalb der Struktur/Klasse befinden:

struct X
{
   void operator+(X);
}

oder außerhalb der struct/class wie:

void operator+(X, X);

aber nicht so

struct X
{
   static void operator+(X, X);
}

Kennt eine Körperschaft Gründe für diese Entscheidung? Warum ist die dritte Form nicht erlaubt? (MSVC gibt einen Syntaxfehler aus). Vielleicht steckt dahinter eine Geschichte?

p.s. Die gleichzeitige Anwesenheit der ersten und der zweiten Definition erzeugt Mehrdeutigkeit:

1>CppTest1.cxx
1>c:\ballerup\misc\cf_html\cpptest1.cxx(39) : error C2593: 'operator +' is ambiguous
1>        c:\ballerup\misc\cf_html\cpptest1.cxx(13): could be 'void B1::operator +(B1 &)'
1>        c:\ballerup\misc\cf_html\cpptest1.cxx(16): or       'void operator +(B1 &,B1 &)'
1>        while trying to match the argument list '(B1, B1)'

Ich verstehe nicht, warum diese Mehrdeutigkeit besser als 1,3 oder 2,3 ist.

38
Kirill Kobelev

Ich habe keine besonderen Kenntnisse über C++ - Diskussionen zu diesem Konzept. Sie können dies also ignorieren.

Aber für mich ist die Frage falsch. Die Frage sollte lauten: "Warum sollte diese Syntax zulässig sein ?"

Es bietet keinerlei Vorteile gegenüber der aktuellen Syntax. Die nicht statische Mitgliedsfunktionsversion hat denselben Zugriff auf private Mitglieder wie Ihre vorgeschlagene statische Version. Wenn Sie also auf die Privaten zugreifen müssen, um sie zu implementieren, machen Sie sie einfach zu einem nicht statischen Mitglied, genau wie Sie es bei den meisten Mitgliedern einer Klasse tun.

Es macht es nicht einfacher, asymmetrische Operatoren zu implementieren (dh: operator+(const X &x, const Y &y)). Wenn Sie privaten Zugriff benötigen, um dies zu implementieren, benötigen Sie in einer der Klassen noch eine Freundschaftserklärung für diese Personen.

Ich würde also sagen, dass der Grund, warum es nicht existiert, darin besteht, dass es nicht notwendig ist . Zwischen Nichtmitgliedsfunktionen und nicht statischen Mitgliedern werden alle erforderlichen Anwendungsfälle behandelt.


Oder anders ausgedrückt:

Freie Funktionen können alles, was das statische Funktionssystem kann und mehr .

Durch die Verwendung von freien Funktionen können Sie für Operatoren, die in Vorlagen verwendet werden, eine argumentabhängige Suche durchführen. Sie können dies nicht mit statischen Funktionen tun, da diese zu einer bestimmten Klasse gehören müssten. Und Sie können einer Klasse außerhalb der Klasse kein hinzufügen, während Sie einem Namespace hinzufügen können. Wenn Sie also einen Operator in einen bestimmten Namespace setzen müssen, damit ADL-Code funktioniert, können Sie dies tun. Mit statischen Funktionsoperatoren ist dies nicht möglich.

Freie Funktionen sind also eine Obermenge von allem, was Ihr vorgeschlagenes statisches Funktionssystem bieten würde. Da es keinen Vorteil hat, es zuzulassen, gibt es keinen Grund , es zuzulassen, und daher ist es nicht zulässig.


was würde es ermöglichen, Funktoren zu verwenden, ohne sie zu instanziieren?

Das ist ein Widerspruch. Ein "Funktor" ist ein "Funktionsobjekt". Ein Typ ist kein Objekt ; Daher kann es kein Funktor sein. Es kann sich um einen Typ handeln, der beim Instanziieren einen Funktor ergibt. Aber der Typ alleine wird kein Funktor sein.

Darüber hinaus würde die Deklaration von Typename::operator() static nicht bedeuten, dass Typename() das tun würde, was Sie wollen. Diese Syntax hat bereits eine tatsächliche Bedeutung: Sie können ein Typename temporär instanziieren, indem Sie den Standardkonstruktor aufrufen.

Schließlich, selbst wenn all das nicht der Fall wäre , was würde das eigentlich nützen? Die meisten Template-Funktionen, die einen Aufruf einer Art annehmen, funktionieren mit einem Funktionszeiger genauso gut wie mit einem Funktor. Warum sollten Sie Ihre Benutzeroberfläche nicht nur auf Funktoren beschränken, sondern auf Funktoren, die keine internen Daten haben können ? Das bedeutet, dass Sie nicht in der Lage sind, Lambdas und so weiter einzufangen.

Was nützt ein Funktor, der keinen Staat enthalten kann? Warum möchten Sie den Benutzer zwingen, "functors" zu übergeben, die keinen Status haben? Und warum möchten Sie verhindern, dass der Benutzer Lambdas verwenden kann?

Ihre Frage geht also von einer falschen Annahme aus: Selbst wenn wir sie hätten, würde sie Ihnen nicht das geben, was Sie wollen.

18
Nicol Bolas

Da es keine offensichtliche Syntax für call solch einen Operator gibt, müssten wir uns etwas einfallen lassen. Betrachten Sie die folgenden Variablen:

X x1;
X x2;

Nehmen wir an, wir machen einen Moment so, als würden wir normale Elementfunktionen anstelle von Operatoren verwenden. Nehmen wir an, ich habe in Ihrem Beispiel operator+ in plus geändert.

Jeder der drei Anrufe würde folgendermaßen aussehen:

x1.plus(x2);
plus(x1, x2);
X::plus(x1, x2);

Wenn Sie nun einen Operator mit + anrufen, woher weiß der Compiler, ob er Ihren Operator im Bereich von X nachschlagen kann? Dies ist für normale statische Elementfunktionen nicht möglich, und den Bedienern wird keine besondere Diskretion erteilt.

Überlegen Sie nun, ob Sie beide die zweite und dritte Form in Ihrem Programm deklariert haben. Wenn Sie x1 + x2 sagten, müsste der Compiler entweder immer die freie Funktion auswählen oder der Aufruf wäre mehrdeutig. Die einzige echte Alternative wäre so etwas wie x1 X::+ x2, der einfach hässlich aussieht. Ich bin mir sicher, dass das Standardisierungskomitee beschlossen hat, die statische Member-Version einfach zu verbieten, da alles, was sie erreichen könnte, stattdessen mit einer Freundesfunktion durchgeführt werden kann.

13
Mark B

Statische Memberfunktionen können für Dienstprogramme verwendet werden, die einer Klasse helfen, aber aus dem einen oder anderen Grund keine Member sind. Man kann sich leicht vorstellen, dass es unter den Dienstprogrammen, die als statische Klassenmitgliedsfunktionen ausgedrückt werden, Operatoren hilfreich sein kann.

Wenn ein überladener Operator eine Klasse C als sein primäres Argument verwendet, gibt es keinen Grund, es als statisches Member der Klasse C zu definieren. Es kann nur ein nicht-statisches Member sein, daher wird dieses Argument implizit abgerufen.

Ein statischer Member der Klasse C kann jedoch ein Operator sein, der in einer anderen Klasse als C überladen ist.

Angenommen, es gibt einen Dateibereich operator ==(const widget &, const widget &);. In meiner squiggle-Klasse arbeite ich mit widget-Objekten, möchte jedoch einen anderen Vergleich für sie. 

Ich sollte in der Lage sein, eine static squiggle::operator == (const widget &, const widget &); für mich selbst zu machen.

Aus dem Klassenbereich ist dies leicht aufzurufen:

void squiggle::memb(widget a, widget b)
{
   if (a == b) { ... } // calls static == operator for widgets
}

von außerhalb des Klassenbereichs können wir ihn nur mit der expliziten Bereichsauflösung in Kombination mit der expliziten Operatoraufrufsyntax aufrufen: 

void nonmemb(widget a, widget b)
{
   a == b;  // calls the widget member function or perhaps nonstatic operator
   squiggle::operator ==(a, b); // calls squiggle class' utility
}

Das ist keine schlechte Idee. Außerdem machen wir can mit normalen überladenen Funktionen, nur nicht mit Operatoren. Wenn der Vergleich von Widgets mit einer compare-Funktion durchgeführt wird, kann es ein Nichtmitglied compare oder ein widget::compare sein, und es kann einen squiggle::compare geben, der widgets verwendet.

Der einzige Aspekt, der in C++ nicht unterstützt wird, ist das syntaktische Zuckern mit Operatoren.

Vielleicht ist es keine ausreichend nützliche Idee, um Support zu garantieren (bisher!). Dies ist nichts, was eine revolutionäre Neuorganisation eines C++ - Programms ermöglichen würde. Aber es würde eine Unvollständigkeit in der Sprache beheben.

Bedenke, dass Klassenüberladungen der Operatoren new und delete implizit statisch sind! Die Unvollständigkeit hat also schon eine kleine Ausnahme.

2
Kaz

hmmm ... Ich denke an einen statischen Operator (), der implizit alle Konstruktoren löschen würde ... Das würde uns typisierte Funktionen geben. Manchmal wünschte ich, wir hätten es in C++.

1
Slava

Mir sind keine direkten Nachteile bewusst, die das Zulassen eines statischen Operators + verursachen könnte (vielleicht wird ein langes Nachdenken zu einer Theorie führen). Aber ich denke, zumindest das von Bjarne Stroustrup festgelegte Prinzip "Bezahle nicht für das, was du nicht verwendest", ist schon eine gute Antwort. Sie müssen überall "X :: operator +" anstelle von "+") schreiben.

0
mvidelgauz

Grundsätzlich kauft ein statischer Operator eines Klassenmitglieds nichts über ein nicht statisches Member.

Jeder für eine Klasse definierte Operator muss mindestens ein Argument dieses Klassentyps enthalten.

Ein Member-Operator übernimmt dieses Argument in Form des impliziten Parameters this.

Ein Nicht-Member-Operator hat ein explizites Argument dieses Klassentyps.

Die Bedienoberfläche zur Bedienfunktion ist egal. Wenn wir a + b aufrufen, muss der Code generiert werden, um a entweder über den Parameter this oder als explizit deklarierter Parameter zu übergeben. Wir äußern also keinen Unterschied in der Nuance zwischen statisch und nicht statisch hinsichtlich der Verwendung des Operators.

Angenommen, es wurde plötzlich eine Anforderung eingeführt, dass das neueste ISO C++ statische Memberoperatoren unterstützen muss. In aller Eile könnte diese Anforderung durch eine Umschreibung von Quelle zu Quelle gemäß dem folgenden Muster implementiert werden:

static whatever class::operator *(class &x) { x.fun(); return foo(x); }

-->

whatever class::operator *() { (*this).fun(); return foo(*this); }

-->

whatever class::operator *() { fun(); return foo(*this); }

Der Compiler schreibt den Member-Operator static in nicht-statisch um, löscht den Parameter ganz links und ersetzt (bei richtiger lexikalischer Hygiene mit Shadow) alle Verweise auf diesen Parameter durch den Ausdruck *this (dessen unnötige Verwendung kann entfallen).

Diese Umwandlung ist so einfach, dass der Programmierer den Code überhaupt so schreiben kann.

Der Mechanismus zur Definition der static-Operatorfunktion ist weniger leistungsfähig. Es kann zum Beispiel nicht virtual sein, wohingegen die nicht statische sein kann.

0
Kaz

Das könnte der Grund sein.

Weil jede operator eine oder mehrere operands benötigt. Wenn wir es also als static deklarieren, können wir es nicht mit Objekten (Operanden) aufrufen. 

Um sie auf einen Operanden aufzurufen, der nur ein Objekt ist, muss die Funktion nicht statisch sein.

Nachfolgend finden Sie eine Bedingung, die erfüllt sein muss, wenn Sie die Funktion überladen.

  • Es muss mindestens einen Operanden mit benutzerdefiniertem Typ haben.

Nehmen wir an, wir erklären die Überladungsfunktion unseres Operators als statisch . Dann ist die erste aller obigen Bedingungen nicht erfüllt.

Ein weiterer Grund ist, dass wir innerhalb statischer Funktionen nur auf statische Datenelemente zugreifen können. Beim Überladen der Operatoren müssen wir jedoch auf alle Datenmitglieder zugreifen. Wenn wir also die Operator-Überladungsfunktion als statisch deklarieren, können wir nicht auf alle Datenelemente zugreifen.

Die Operatorüberladungsfunktion muss also ein non-static member function sein.

Aber da gibt es eine Ausnahme.

Wenn wir eine Friend-Funktion zur Überladung von Operatoren verwenden, kann sie als statisch deklariert werden.

0
Narendra