Warum ist der Pfeiloperator in C ++ nicht nur ein Alias ​​von *.?


18

In c ++ kann der Operator * überladen werden, z. B. mit einem Iterator. Der Pfeiloperator (->) (. *) Funktioniert jedoch nicht mit Klassen, die den Operator * überladen. Ich stelle mir vor, dass der Präprozessor leicht alle Instanzen von -> durch (* left) .right ersetzen könnte, und das würde die Implementierung von Iteratoren angenehmer machen. Gibt es einen praktischen Grund für -> anders zu sein, oder ist das nur eine Besonderheit der Sprache / Designer?

Antworten:


16

Die Regel foo->bargleich gilt (*foo).barnur für die eingebauten Operatoren.

Unary operator *hat nicht immer die Semantik der Zeiger-Dereferenzierung. Ich könnte eine Bibliothek erstellen, in der es sich um Matrixtransposition, null oder mehr Parser-Übereinstimmungen oder so ziemlich alles handelt.

Es würde die Sprache störender machen, wenn irgendetwas, das unärgerlich ist operator *, plötzlich einen Wert erhält, nach dem operator ->Sie nicht gefragt haben, mit einer Semantik, die möglicherweise keinen Sinn ergibt.

operator -> ist separat überlastbar. Wenn Sie also eines möchten, können Sie eines mit minimalem Aufwand überlasten.

Beachten Sie auch, dass eine solche Überladung einige interessante Eigenschaften haben würde, z. B. das automatische Verketten von operator ->Aufrufen, bis einer in der Kette einen rohen Zeiger zurückgibt. Dies ist sehr nützlich für Smart Pointer und andere Proxy-Typen.

#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <string>
#include <iostream>
#include <ostream>

struct Foo
{
    boost::shared_ptr<std::string> operator -> () const
    {
        return boost::make_shared<std::string>("trololo");
    }
};

int main()
{
    Foo foo;
    std::cerr << foo->size() << std::endl;
}

Was zeigt Ihr Beispiel? Geben Sie einen intelligenten Zeiger auf eine Zeichenfolge zurück und geben Sie die Größe irgendwie aus? Ich bin verwirrt.
Trevor Hickey

2
Es zeigt den letzten Absatz meiner Antwort, wie man die ->Operatorketten verwendet, bis man einen rohen Zeiger auf etwas erhält, dereferenziert und auf ein Mitglied davon zugreift. Wenn der Operator -> nicht verkettet wäre, wäre das Beispiel schlecht geformt, da shared_ptr kein roher Zeiger ist.
Lars Viklund

@ LarsViklund: Ihre Antwort hat ein Problem: Sie sagten "operator-> ... verkettet automatisch operator-> Anrufe, bis einer in der Kette einen rohen Zeiger zurückgibt". Dies ist nicht korrekt - mit A->BSyntaxketten höchstens 1 zusätzlicher Aufruf. Was die C ++ -> Binärsyntax tatsächlich bewirkt, ist, dass das Objekt nicht opeartor->direkt aufgerufen wird. Stattdessen wird der Typ des Objekts untersucht Aund geprüft, ob es sich um einen rohen Zeiger handelt. Wenn es dann ->derefs ist und darauf ausgeführt wird B, andernfalls ruft es das Objekt auf operator->, derefs das Ergebnis (entweder unter Verwendung eines nativen rohen Zeigers oder eines anderen) operator->und führt dann Bdas Ergebnis aus
Guss

@Guss: Ich kann kein Kapitel und keinen Vers für Ihre Behauptung finden oder in einem Compiler reproduzieren. C ++ 11 13.5.6 / 1 gibt an, dass bei Vorliegen einer geeigneten Überlast x->mzu interpretieren ist als (x.operator->())->m. Wenn das LHS operator->erneut eine geeignete Überlastung aufweist, wiederholt sich dieser Vorgang, bis nur noch der übliche (*x).mEffekt von 5.2.5 / 2 vorliegt.
Lars Viklund

8

"The C ++ Programming Language" beschreibt die Tatsache, dass diese Operatoren unterschiedlich sind, so dass sie sein können, sagt aber auch:

Wenn Sie mehr als einen dieser Operatoren angeben, ist es möglicherweise ratsam, die Äquivalenz anzugeben, genauso wie es sinnvoll ist, dies sicherzustellen ++xund x+=1dieselbe Wirkung wie x=x+1für eine einfache Variable xeiner Klasse zu erzielen, wenn ++, + =, = und + sind vorhanden.

Es scheint also, dass die Sprachentwickler separate Überladungspunkte bereitgestellt haben, weil Sie sie möglicherweise anders überladen möchten, anstatt davon auszugehen, dass sie immer gleich sein sollen.


7

In der Regel ist C ++ so konzipiert, dass Flexibilität bevorzugt wird. Überladungen von *und ->sind daher getrennt. Obwohl dies ziemlich ungewöhnlich ist, können Sie diese Überladungen schreiben, um ganz andere Dinge zu tun (z. B. kann dies für eine domänenspezifische Sprache, die in C ++ implementiert ist, sinnvoll sein).

Das sei gesagt, Iteratoren tun entweder Nutzung unterstützen. Auf alte Implementierungen, könnten Sie eine Bibliothek , die erfordert (*iter).whateverstatt iter->whatever, aber wenn ja, das ist ein Fehler in der Implementierung, kein Merkmal der Sprache. Angesichts des Arbeitsaufwands bei der Implementierung aller Standardcontainer / -algorithmen / -iteratoren ist es nicht verwunderlich, dass einige frühe Releases etwas unvollständig waren, aber eigentlich nie so gedacht waren.


Ich habe nicht bemerkt, dass Standard-Bibliothekscontainer implementiert sind -> oder dass sie überlastbar sind.
Jakob Weisblat

3
In C ++ 03 24.1 / 1 muss jeder (*i).mgültige Iterator i->mdieselbe Semantik unterstützen.
Lars Viklund
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.