2013-08-05 15 views
12

Warum enthält der C++ - Standard keine Vergleichsoperatoren, um intelligente Zeiger (unique_ptr, shared_ptr, ...) mit regulären Zeigern (T *) zu vergleichen?Kein Standard Weg, Smart Pointer mit regulären Zeiger zu vergleichen?

Tom

Update ich nicht herausfinden, bin auf der Suche, wie es gemacht werden kann. Die Frage ist, warum ist es nicht als Teil des C++ Standards definiert? Für unique_ptr und shared_ptr wären solche Definitionen trivial.

Ein Anwendungsfall dafür ist der folgende: Klasse A verfügt über eine Zuordnung mit eindeutigen Schlüssel. unique_ptr wird für die Speicherverwaltung verwendet. Wenn ein Benutzer der Klasse A einen regulären Zeiger übergeben hat, wird in dieser Map eine Suche durchgeführt. Leider definiert der Standard die Vergleichsoperatoren nicht.

+2

Intelligente Zeiger sind nicht dasselbe wie rohe Zeiger. Warum Äpfel mit Birnen vergleichen? –

+1

@TonyTheLion um herauszufinden, welches ist lecker, duh.(Hinweis: Wenn es ein wirklich guter Apfel ist, dann schraube Birnen) :) – jalf

+0

Übrigens, auch wenn diese Vergleiche möglich wären, würde das Ihren speziellen Anwendungsfall, der durch die 'std :: map begrenzt ist, noch nicht lösen 's Schnittstelle und nicht nur durch die für den Schlüsseltyp im Allgemeinen erlaubten Vergleiche. Zu Ihrer Information: Das gleiche Problem (die Suche nach einem 'std :: map ' mit einem rohen Zeiger) wurde erst vor kurzem hier angezeigt. –

Antwort

14

Warum funktioniert die C++ Standard sind Vergleichsoperatoren intelligente Zeiger vergleichen (unique_ptr, shared_ptr, .. .) mit regulären Zeigern (T *)?

Das Prinzip hinter dieser Entscheidung wird in der Regel als „machen Ihre Schnittstellen einfach zu bedienen korrekt und schwer/unmöglich zu verwenden falsch“ angegeben.

Konzeptionell sind ein intelligenter Zeiger und ein roher Zeiger nicht identisch.

Ein intelligenter Zeiger auferlegt Einschränkungen (d. H. "Unique_ptr ist ein Zeiger, aber Sie können nicht mehrere Kopien haben"). Obwohl sie sich wie Zeiger verhalten (bis zu einem gewissen Punkt - wenn Sie das Wortspiel entschuldigen wollen), haben sie unterschiedliche Semantiken.

Das heißt, wenn Sie:

int *p1 = new int{5}; 
std::unique_ptr<int> p2{new int{5}}; 

p1 == p2.get(); 

Der Vergleich einfach zu tun ist, ausdrücklich, und macht es offensichtlich, dass Sie Äpfel und Äpfel (leicht zu verstehen, zu vergleichen, was los ist - Sie vergleichen mit dem Wert eines rohen Zeigers).

Ein benutzerdefinierter Vergleichsoperator andererseits würde merkwürdige Fragen aufwerfen ("ein unique_ptr ist einzigartig; wie kann man es mit etwas anderem vergleichen? - wenn es einzigartig ist, sollte es immer anders sein").

+1

"Wenn es einzigartig ist, sollte es immer anders sein" 'auto & rp0 = p2; auto & rp1 = p2; p1 == p2' (Stellen Sie sich Funktionsparameter vor) Es gibt Vergleichsoperatoren für 'std :: unique_ptr' und andere' unique_ptr's oder 'nullptr_t'. Ersteres vergleicht tatsächlich den eigenen Zeiger, dh 'p2 == p2': <=>' p2.get() == p2.get() ', daher wäre der Vergleich' p1 == p2.get() 'wohldefiniert und nicht überraschend. – dyp

+0

"wäre wohldefiniert und nicht überraschend" - das ist eine alte Debatte, die von Sprachdesignern auf unterschiedliche Weise angesprochen wird (d. H. "Objekte sind gleich vs. Objekte haben gleiche Werte"); In Java ist es die Quelle des String-Vergleichsproblems (Sie vergleichen Strings mit 's1.compare (s2)', nicht mit 's1 == s2'). Wenn Sie einen 'operator ==' mit 'std :: unique_ptr ' und 'T *' erstellen, können Sie alle Clients Ihres Codes nicht als konzeptionell anders betrachten (weil sie unterschiedliche Typen haben), es sei denn, sie schreiben hässlich/fehleranfälligen Code dafür (zum Beispiel den Typ zuerst vergleichen). – utnapistim

+0

Was ich meinte war: der 'operator ==' für zwei 'unique_ptr's ist bereits allein in Bezug auf die eigenen Zeiger definiert. Daher muss sich der Vergleich mit einem rohen Zeiger nicht anders verhalten. – dyp

8

Sie können einfach tun smart_ptr.get() == raw_ptr, was ist los damit?

+1

Ich weiß, dass das funktioniert. Warum erlaubt der Standard keinen direkten Vergleich? Betrachten Sie zum Beispiel das Nachschlagen in einer Karte, die einen Smart-Pointer-Schlüsseltyp aufweist. Sie müssen Ihren eigenen Komparator definieren, damit er richtig funktioniert. –

+0

Es bringt Klarheit für mich. Dies zu lesen ist einfacher, da Sie nicht so viele Informationen benötigen. Wie auch immer - das Hinzufügen eines eigenen Operators == als freie Funktion sollte kein Problem sein. –

+3

@MasterT Falsche Frage. Warum sollte es? –

4

Da die Vergleichsoperatoren nicht vergleichen, worauf gezeigt wird, nur der tatsächliche Zeiger. Und da die Smart Pointer "Besitz" des tatsächlichen Roh-Pointers haben, kann jeder andere Roh-Pointer nicht mit dem Smart-Pointer verglichen werden.

+0

Rohe Zeiger können/sollten von Teilen des Programms verwendet werden, die das Objekt nicht am Leben erhalten müssen. Würdest du beim Vergleich darauf achten, wer das Objekt am Leben hält? –

+1

@MasterT Ja natürlich tun Sie. Warum also Smartpointer verwenden? Natürlich erfordern verschiedene Operationen unterschiedliche Bedeutungen von Gleichheit. Aber die * allgemeine * Bedeutung sollte definitiv Besitz (und möglicherweise Deleter) erklären. –

+0

@KonradRudolph Warum sollten Ownership und Deleters beim Vergleich von Smartpointern berücksichtigt werden? Beim Besitz geht es darum, wann gelöscht werden soll und bei Deletern geht es darum, wie sie gelöscht werden. Meiner Meinung nach gibt es keine semantische Beziehung zwischen Löschen und Vergleichen. –

1

Der Hauptgrund ist wahrscheinlich, weil, wenn Sie die Standard-Smart-Pointer verwenden, sollte es keine rohen Zeiger auf das Objekt geben. Einen rohen Zeiger auf das Objekt zu haben, ist fast eine Garantie, dass jemand irgendwann während der Wartung einen zweiten intelligenten Zeiger davon mit unheilvollen Folgen enden wird. (Die einzige Ausnahme ist ein Null-Zeiger und der Standard tut Vergleich mit einem Null-Zeiger-Konstante ermöglichen.)

+0

Wenn ein Teil des Programms nicht über die Lebensdauer von Objekten wissen muss, neige ich dazu, normale Zeiger in den Klassenschnittstellen zu verwenden. Siehe http://stackoverflow.com/questions/7657718/when-to-use-shared-ptr-and-when-to-use-raw-pointers –

+3

Hammer nur nach Hause, dass jeder rohe Zeiger ist nicht besitzend, sollte es sein die Standardannahme über sie mit C++ 11. – Xeo

+0

@Xeo Der übliche Fall ist, dass es keine besitzenden Zeiger gibt, Periode, und es gibt keine Verwendung für irgendwelche intelligenten Zeiger. Das Konzept eines Pointers, der etwas besitzt, ist ein bisschen komisch; Zeiger sind Objekte auf sehr niedriger Ebene, ohne wirkliches Verhalten oder Verantwortlichkeiten. Die meisten Anwendungen werden keine Smart Pointer verwenden. Auf der anderen Seite ist das Mischen von 'std :: shared_ptr' und rohen Zeigern ein Rezept für ein Desaster. Wenn Sie 'std :: shared_ptr' verwenden, sollten Sie keinen rohen Zeiger auf das Objekt haben (was natürlich bedeutet, dass kein Element funktioniert, da' dies' ein roher Zeiger ist). –