Frage Namensräume und Betreiberauflösung


Ich verwende eine Bibliothek, die Ausgabestream-Operatoren (Operator <<) im globalen Namespace definiert. In meinem eigenen Namensraum deklarierte ich solche Operatoren immer im globalen Namensraum und hatte nie Probleme damit. Aber jetzt muss ich aus verschiedenen Gründen diese Operatoren in meinem eigenen Namespace deklarieren, und plötzlich scheint der Compiler die in der Bibliothek deklarierten Operatoren nicht mehr zu finden.

Hier ist ein einfaches Beispiel, das mein Problem veranschaulicht:

#include <iostream>

namespace A
{
   struct MyClass {};
}

std::ostream & operator<<( std::ostream & os, const A::MyClass & )
   { os << "namespace A"; return os; }

namespace B
{
   struct MyClass {};

   std::ostream & operator<<( std::ostream & os, const B::MyClass & )
      { os << "namespace B"; return os; }
}

namespace B
{
   void Test()
   {
      std::cout << A::MyClass() << std::endl;
      std::cout << B::MyClass() << std::endl;
   }
}

int main()
{
   B::Test();
   return 1;
}

Ich erhalte den folgenden Fehler:

error: no match for ‘operator<<’ in ‘std::cout << A::MyClass()’

Beachten Sie, dass der Code kompiliert und ordnungsgemäß ausgeführt wird, wenn sich beide Operatoren innerhalb der Namespaces befinden oder wenn beide sich in dem globalen Namespace befinden.

Ich würde gerne verstehen, was vor sich geht und was die "gute Praxis" ist, solche Operatoren mit Namespaces zu definieren.

Vielen Dank!


32
2018-03-04 15:12


Ursprung


Antworten:


Schon seit Test ist innerhalb des Namensraums B Die Kompilierung sieht den Operator in diesem Namespace und stellt fest, dass er keine übereinstimmende Signatur hat. Es versucht auch, den Operator im Namensraum A zu finden, der die Klasse enthält, sie aber dort auch nicht finden kann. Weil es im Namespace bereits einen solchen Operator (mit der falschen Signatur) gibt B es wird nicht gehen und versuchen, einen auf globaler Ebene zu finden.

Der Grund, warum es nicht nach dem globalen sucht, ist ungefähr wie folgt. Ich werde zuerst den Standard zitieren und dann versuchen, es zu erklären.

Ab 3.4 / 1:

... Name-Lookup kann mehr als assoziieren   eine Erklärung mit einem Namen, wenn es   findet den Namen als Funktionsnamen;   Die Erklärungen sollen ein   Satz überladener Funktionen (13.1).   Überladungsauflösung (13.3) findet statt   Nach dem Namenssuche ist erfolgreich.

Wenn ich das hier lese, versucht der Compiler, eine Funktion zu finden (die Ihre Operatoren in diesem Kontext sind), zuerst nach der Namenssuche zu suchen, um die Funktion zuerst zu finden. Dann versucht es als nächstes die richtige Funktion aus der Menge der Überladungen auszuwählen.

Ab 3.4.1 / 6:

Ein Name, der in der Definition von a verwendet wird   Funktion (26), die ein Mitglied von   Namensraum N (wo, nur für die   Zweck der Exposition, N könnte   den globalen Geltungsbereich darstellen)   vor seiner Verwendung im Block deklariert   in dem es verwendet wird oder in einem seiner   umschließende Blöcke (6.3) oder, soll sein   vor seiner Verwendung im Namensraum N deklariert   oder, wenn N ein verschachtelter Namespace ist,   vor seiner Verwendung in einem von erklärt werden   N's umschließende Namespaces.

Lass uns das brechen. Du benutzt das operator<< In einer Namespace-Level-Funktion gilt dieser Abschnitt. Es wird versuchen, diesen Operator zu finden, der die Priorität in dem oben beschriebenen verwendet. Ihr Operator ist weder im aktuellen Block noch in den umschließenden Blöcken deklariert (dies bezieht sich auf verschachtelt) {} innerhalb Ihrer Funktion). Der nächste Teil entspricht jedoch "... soll vor seiner Verwendung im Namensraum N deklariert werden ...". Dort ist in der Tat ein operator<< im aktuellen Namespace (B) so fügt es diesen Operator zu seiner Liste der Übereinstimmungen hinzu. Es gibt keine weiteren Übereinstimmungen in BDa der Bereich mit dem gleichen Namespace als die bestmögliche Übereinstimmungsnähe betrachtet wird, werden keine anderen Bereiche berücksichtigt.

Der Grund, warum es funktioniert, wenn Sie den Operator in Namespace A einfügen, ist, dass das Element, von dem gedruckt wird, Mitglied von ist ADieser Namespace wird tatsächlich berücksichtigt, da er in den Namespaces des Ausdrucks enthalten ist. Seit dem Namensraum A  ist berücksichtigt, dass es die entsprechende Übereinstimmung in diesem Namespace findet und korrekt kompiliert.

Jetzt, da es eine Liste von möglichen Operatoren gibt, versucht es, eine Überladungsauflösung für sie durchzuführen. Leider ist die in Namespace B gefundene Komponente die einzige, die sie berücksichtigt, und sie stimmt nicht mit den erforderlichen Argumenten überein.

Im Allgemeinen sollten Sie die Insertion-Operatoren im selben Namespace haben wie die Klasse, auf der sie operiert.


33
2018-03-04 15:24



Meine Antwort ist den anderen sehr ähnlich, aber insbesondere versucht der Compiler, den A :: operator << () zu finden, weil er auf etwas im A-Namespace arbeitet. Wenn Sie das Objekt außerhalb des Namensraums aufrufen möchten, können Sie es explizit mit verwenden

::operator<<(std::cout, A::MyClass();

Für eine reibungslosere syntaktische Verwendung, lege es in den Namespace.


10
2018-03-04 15:58



Das Problem wurde in der Antwort von @Mark B erklärt. Das Folgende löst das Problem. In dem Namespace, in dem Sie das Globale verwenden möchten operator<<, geben Sie den folgenden Code ein:

using ::operator<<;

Im Codebeispiel des OP würde diese Codezeile an den Speicherort entlang des anderen Codes gehen, der deklariert / definiert operator<< zum namespace B:

namespace B
{
   struct MyClass {};

   std::ostream & operator<<( std::ostream & os, const B::MyClass & )
      { os << "namespace B"; return os; }

   using ::operator<<;
}

7
2017-09-28 11:54



Es ist weil dein erster operator<<() ist außerhalb des Namensraums A definiert.


0
2018-03-04 15:21