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 weiterhin std::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 dem std-Namensbereich - und daher der Lookup nur in std nachschaut.
    Schau mal in C++ Lookup Mysteries (Stichwort: PrintSpannerNameAndGap).

    Kannst du keine Wrapper-Klasse (für VecStr) in NS 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 bei

    template <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 einen std-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ür operator<< 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 den operator<< 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.


Anmelden zum Antworten