Weiteren namespace bei ADL hinzufügen
-
Hallo,
dämlicher Titel...ich habe folgendes Problem:
namespace NS { using VecStr = std::vector<std::string>; std::ostream& operator <<(std::ostream& os, const NS::VecStr& o) {os << "MachWasSchönes"; return os;} } template <typename T> void g(const T& a) { std::cout << a; // Error: <<-operator wird nicht gefunden }
Der Operator wird nicht gefunden, da ADL hier nicht nach NS guckt (wo es deklariert ist), sondern nach dem wahren Typ (in std).
Den std-namespace zu ergänzen ist UB (soweit ich weiss) und irgendein using XYZ in der g-Funktion geht nicht, da T aus verschiedenen namespaces kommen kann.Sieht jemand eine Möglichkeit, wie ich die Funktion dazu kriege im richtigen namespace zu schauen?
-
@Jockelx sagte in Weiteren namespace bei ADL hinzufügen:
Der Operator wird nicht gefunden, da ADL hier nicht nach NS guckt (wo es deklariert ist), sondern nach dem wahren Typ (in std).
Ja. Das ist auch korrekt so.
VecStr
ist bloss ein Alias-Name, ändert aber am Typ nichts. Der Typ ist weiterhinstd::vector<std::string>
. Und zwar nicht der "wahre" Typ, sondern der einzige Typ.Den std-namespace zu ergänzen ist UB (soweit ich weiss) und irgendein using XYZ in der g-Funktion geht nicht, da T aus verschiedenen namespaces kommen kann.
Sieht jemand eine Möglichkeit, wie ich die Funktion dazu kriege im richtigen namespace zu schauen?
So direkt geht das nicht.
-
Ja, das Problem besteht darin, daß du keine eigene Klasse benutzt, sondern mittels
using VecStr = std::vector<std::string>;
direkt eine Klasse aus demstd
-Namensbereich - und daher der Lookup nur instd
nachschaut.
Schau mal in C++ Lookup Mysteries (Stichwort:PrintSpannerNameAndGap
).Kannst du keine Wrapper-Klasse (für
VecStr
) inNS
verwenden?
-
@Th69 sagte in Weiteren namespace bei ADL hinzufügen:
Kannst du keine Wrapper-Klasse (für VecStr) in NS verwenden?
Ja, das ist auch meine Lösung.
Allerdings ist das doch aufwendiger als zunächst gedacht und ich wollte hier nochmal auf Nummer sicher gehen, dass ich nicht etwas vergleichsweise einfaches übersehe.
-
Nur als Frage (nicht, das ich dies wirklich empfehlen würde).
Was passiert denn beitemplate <typename T> void g(const T& a) { // Edit: using namespace NS; //using namespace std; std::cout << a; }
?
Oder hast du auch Alias-Namen aus anderen Namensbereichen?
-
@Th69 Nee, das Problem ist ja gerade, dass er in std sucht und nicht in NS. Ich bräuchte also using namespace NS und das geht nicht, weil es mehrere namespaces gibt.
-
Sorry, meinte natürlich
using namespace NS;
.Habe es aber gerade selber ausprobiert und dann funktioniert es.
Generelles Problem ist jedoch, daß ja in jedem möglichen Namensbereich es eine Operator<<
-Überladung für einenstd
-Datentyp geben könnte und dann wäre es nicht mehr eindeutig: Compiler Explorer Beispiel.Du müßtest ja explizit z.B.
NS::VecStr v; // bzw. std::vector<string> NS::operator <<(std::cout, v);
aufrufen, um dies zu unterscheiden, s. Weiteres Compiler Explorer Beispiel.
Und dies kannst du ja in einer Templatefunktion nicht erzwingen, wenn der Datentyp selber nicht aus dem selben Namensbereich stammt.
-
@Th69 sagte in Weiteren namespace bei ADL hinzufügen:
Sorry, meinte natürlich
using namespace NS;
.Sogar das geht auch nicht - zumindest nicht gut.
So, in dieser Reihenfolge geht es:
#include <iostream> struct Foo{}; namespace NS { std::ostream& operator<<(std::ostream& os, Foo const& foo) { return os << "meh"; } } // namespace NS template <typename T> void g(const T& a) { using namespace NS; std::cout << a; } int main() { Foo f; g(f); }
Aber so geht es schon nicht mehr:
#include <iostream> namespace NS {} template <typename T> void g(const T& a) { using namespace NS; std::cout << a; } // Später, z.B. weil in anderem Header File definiert: struct Foo{}; namespace NS { std::ostream& operator<<(std::ostream& os, Foo const& foo) { return os << "meh"; } } // namespace NS int main() { Foo f; g(f); }
Der Grund ist, dass bereits beim Parsen von
g
die Liste der in Frage kommenden Funktionen füroperator<<
erstellt wird. Und diese wird später, bei der Instanzierung, nur mehr um per ADL gefundene Funktionen erweitert. In anderen Namespaces wie hier z.B.NS
wird nicht nochmal nachgesehen.p.S.:
Es hilft nichtmal denoperator<<
im globalen Namespace zu definieren -- ausser in dem Spezialfall dass der auszugebende Typ im globalen Namespace lebt. Gleicher Grund: es wird nur in Namespaces nachgesehen die per ADL gefunden werden, und wenn der globale Namespace halt nicht per ADL gefunden wird, dann guckt der Compiler dort auch nicht nochmal nach.