Damit der Bediener überlastet wird, muss die Funktion funktionieren im selben Namespace wie einer seiner Operanden. Ansonsten, ADL findet es nicht. Dies bedeutet den Namensraum Ihrer Klasse für Operatoren wie + und -. Theoretisch könnten Sie den Operator & lt; & lt; entweder in std oder im selben Namespace wie Ihre Klasse, aber die Standard verbietet das Definieren neuer Funktionen in Std, also auch hier setze es in den gleichen Namespace wie die Klasse.
(Und natürlich implementieren Sie nicht + oder -, sondern + = und - =, und dann von einer Vorlage abgeleitet werden, die + und liefert - automatisch.)
Beim Durchsuchen von SO finde ich oft Fragen oder Antworten, die ein Überladen / Definieren von %code% oder %code% beinhalten.
Während ich weiß, wie und wann (nicht) ich diese Operatoren schreiben soll, bin ich verwirrt über das Thema %code% .
Wenn ich die folgende Klasse habe:
%Vor%In welcher %code% sollte ich die verschiedenen Operatordefinitionen schreiben?
%Vor%Dieselbe Frage gilt für %code% . Also, was ist die gute Praxis hier und warum ?
Die beste Wahl ist Option 1. Warum? Wenn Sie einen unqualifizierten Funktionsnamen verwenden (ein überladener Operator ist eine Funktion), wird neben dem normalen Namenssuchen eine argumentabhängige Suche angewendet, dh (informell) alle Namespaces, in denen die Argumente deklariert wurden, werden durchsucht. ZB
%Vor%Das gleiche gilt für Operatoren.
Andererseits wird im Fall von Option 2 der Operator immer noch gefunden, weil ostream in std ist (gleiche ADL-Regel). Aber es ist keine gute Idee, dem Namespace std Dinge hinzuzufügen.
Und die dritte Option ist schlecht, stilistisch - warum, wenn die erste Option ausreicht?
Also, definitiv Option 1.
HTH.
Es sollte im Namensraum %code% sein. Sie müssen überlegen, was die Oberfläche für die Klasse ausmacht und diese zusammen gruppieren.
"Eine Klasse beschreibt eine Reihe von Daten zusammen mit den Funktionen, die mit diesen Daten arbeiten." Ihre kostenlose Funktion arbeitet mit %code% , daher ist sie Teil von %code% . Es sollte mit %code% im Namespace %code% gruppiert werden.
Argumentabhängige Lookup , oder ADL, finden die Funktion.
Wir wissen auch, dass Nicht-Freund-Nichtmitgliedsfunktionen bevorzugen . Das bedeutet, dass Ihre Klassen im Allgemeinen ihre Definitions- und Mitgliedsfunktionen haben, unmittelbar gefolgt von freien Funktionen, die auf der Klasse funktionieren.
Es empfiehlt sich, die (Nicht-Member-) Operatoren im selben Namespace wie die Klasse zu deklarieren, deren Schnittstelle sie angehören.
Für etwas wie %code% ist das ziemlich einfach: Es funktioniert nur auf Foo-Objekten, also sollte es im selben Namensraum wie Foo selbst laufen. Für %code% und %code% können Sie immer noch zwischen den Namespaces %code% und %code% wählen. Zuallererst sollten Sie dem Namespace %code% keine Funktion / Operator-Überladungen hinzufügen. Und zweitens besteht der wichtige Teil dieser Überladungen nicht darin, dass sie mit einem Stream arbeiten, sondern dass sie ein Foo-Objekt lesen / schreiben. Es macht also mehr Sinn, sie mit der Foo-Klasse zu bündeln.
Es sollte auch angemerkt werden, dass die Regeln von C ++ so entworfen sind, dass überladene Operatoren, die im selben Namespace definiert sind wie die Klasse, auf der sie arbeiten, fast immer korrekt gefunden werden, während dies viel öfter schief gehen wird, wenn Operatoren werden in einem anderen, nicht verwandten Namespace deklariert.
Die Regel besteht darin, dass bei der Suche nach einer geeigneten Funktionsüberladung sowohl der aktuelle Namespace als auch alle Namespaces der Argumenttypdefinitionen berücksichtigt werden. Dies nennt sich Argument Dependent Lookup (ADL).
Also wenn du diesen Code hast:
%Vor%Die folgenden Namespaces werden berücksichtigt:
Also alle drei Möglichkeiten, die du genannt hast, funktionieren und sind daher auf den ersten Blick "gut genug".
Wie auch immer ....
Sie dürfen keine neuen Funktionen in :: std definieren, damit Sie Ihren überladenen Operator nicht in diesen Namespace setzen können. (Sie dürfen Vorlagen in :: std spezialisieren, aber das ist nicht, was wir hier tun)
Zweitens kann sich der "aktuelle Namespace" ändern. Wenn Sie also Ihre Funktionsdefinition in diesen Namespace stellen, wird sie möglicherweise nicht immer gefunden.
Am Ende befindet sich der beste Ort, um den überladenen Operator zu platzieren, im selben Namensraum wie Foo:
%Vor% Beim Durchsuchen von SO finde ich oft Fragen oder Antworten, die ein Überladen / Definieren von std::ostream& operator<<(std::ostream& os, const Foo& foo)
oder Foo operator+(const Foo& l, const Foo& r)
beinhalten.
Während ich weiß, wie und wann (nicht) ich diese Operatoren schreiben soll, bin ich verwirrt über das Thema namespace
.
Wenn ich die folgende Klasse habe:
%Vor% In welcher namespace
sollte ich die verschiedenen Operatordefinitionen schreiben?
Dieselbe Frage gilt für operator+
. Also, was ist die gute Praxis hier und warum ?
Es sollte im Namensraum bar
sein. Sie müssen überlegen, was die Oberfläche für die Klasse ausmacht und diese zusammen gruppieren.
"Eine Klasse beschreibt eine Reihe von Daten zusammen mit den Funktionen, die mit diesen Daten arbeiten." Ihre kostenlose Funktion arbeitet mit Foo
, daher ist sie Teil von Foo
. Es sollte mit Foo
im Namespace bar
gruppiert werden.
Argumentabhängige Lookup , oder ADL, finden die Funktion.
Wir wissen auch, dass Nicht-Freund-Nichtmitgliedsfunktionen bevorzugen . Das bedeutet, dass Ihre Klassen im Allgemeinen ihre Definitions- und Mitgliedsfunktionen haben, unmittelbar gefolgt von freien Funktionen, die auf der Klasse funktionieren.
Die Regel besteht darin, dass bei der Suche nach einer geeigneten Funktionsüberladung sowohl der aktuelle Namespace als auch alle Namespaces der Argumenttypdefinitionen berücksichtigt werden. Dies nennt sich Argument Dependent Lookup (ADL).
Also wenn du diesen Code hast:
%Vor%Die folgenden Namespaces werden berücksichtigt:
Also alle drei Möglichkeiten, die du genannt hast, funktionieren und sind daher auf den ersten Blick "gut genug".
Wie auch immer ....
Sie dürfen keine neuen Funktionen in :: std definieren, damit Sie Ihren überladenen Operator nicht in diesen Namespace setzen können. (Sie dürfen Vorlagen in :: std spezialisieren, aber das ist nicht, was wir hier tun)
Zweitens kann sich der "aktuelle Namespace" ändern. Wenn Sie also Ihre Funktionsdefinition in diesen Namespace stellen, wird sie möglicherweise nicht immer gefunden.
Am Ende befindet sich der beste Ort, um den überladenen Operator zu platzieren, im selben Namensraum wie Foo:
%Vor%Damit der Bediener überlastet wird, muss die Funktion funktionieren im selben Namespace wie einer seiner Operanden. Ansonsten, ADL findet es nicht. Dies bedeutet den Namensraum Ihrer Klasse für Operatoren wie + und -. Theoretisch könnten Sie den Operator & lt; & lt; entweder in std oder im selben Namespace wie Ihre Klasse, aber die Standard verbietet das Definieren neuer Funktionen in Std, also auch hier setze es in den gleichen Namespace wie die Klasse.
(Und natürlich implementieren Sie nicht + oder -, sondern + = und - =, und dann von einer Vorlage abgeleitet werden, die + und liefert - automatisch.)
Die beste Wahl ist Option 1. Warum? Wenn Sie einen unqualifizierten Funktionsnamen verwenden (ein überladener Operator ist eine Funktion), wird neben dem normalen Namenssuchen eine argumentabhängige Suche angewendet, dh (informell) alle Namespaces, in denen die Argumente deklariert wurden, werden durchsucht. ZB
%Vor%Das gleiche gilt für Operatoren.
Andererseits wird im Fall von Option 2 der Operator immer noch gefunden, weil ostream in std ist (gleiche ADL-Regel). Aber es ist keine gute Idee, dem Namespace std Dinge hinzuzufügen.
Und die dritte Option ist schlecht, stilistisch - warum, wenn die erste Option ausreicht?
Also, definitiv Option 1.
HTH.
Es empfiehlt sich, die (Nicht-Member-) Operatoren im selben Namespace wie die Klasse zu deklarieren, deren Schnittstelle sie angehören.
Für etwas wie operator+
ist das ziemlich einfach: Es funktioniert nur auf Foo-Objekten, also sollte es im selben Namensraum wie Foo selbst laufen.
Für operator<<
und operator>>
können Sie immer noch zwischen den Namespaces std
und bar
wählen. Zuallererst sollten Sie dem Namespace std
keine Funktion / Operator-Überladungen hinzufügen. Und zweitens besteht der wichtige Teil dieser Überladungen nicht darin, dass sie mit einem Stream arbeiten, sondern dass sie ein Foo-Objekt lesen / schreiben. Es macht also mehr Sinn, sie mit der Foo-Klasse zu bündeln.
Es sollte auch angemerkt werden, dass die Regeln von C ++ so entworfen sind, dass überladene Operatoren, die im selben Namespace definiert sind wie die Klasse, auf der sie arbeiten, fast immer korrekt gefunden werden, während dies viel öfter schief gehen wird, wenn Operatoren werden in einem anderen, nicht verwandten Namespace deklariert.
Tags und Links c++ namespaces operator-overloading operator-keyword