Bug im VC++7 (Operator Lookup)



  • Hallo Leute!

    Ich verstehe die Welt nicht mehr!

    Folgendes geht:

    namespace foo2
    {
    template<class StringT>
    class C
    {
    };
    
    template<class StringT>
    inline std::basic_ostream<typename StringT::value_type, typename StringT::traits_type>& operator<<(std::basic_ostream<typename StringT::value_type, typename StringT::traits_type>& out, C<StringT> const& obj)
    {
        return out;
    }
    }
    
    #include <iostream>
    #include <string>
    using namespace std;
    
    int main()
    {
        //cout<<foo1::C<string>();
        cout<<foo2::C<string>();
    }
    

    Allerdings geht folgendes nicht:

    namespace foo1
    {
    template<class StringT>
    class C
    {
    public:
        std::basic_ostream<typename StringT::value_type, typename StringT::traits_type>& writeAt(std::basic_ostream<typename StringT::value_type, typename StringT::traits_type>& stream) const
        {
            return stream;
        }
    };
    
    template<class StringT>
    inline std::basic_ostream<typename StringT::value_type, typename StringT::traits_type>& operator<<(std::basic_ostream<typename StringT::value_type, typename StringT::traits_type>& out, C<StringT> const& obj)
    {
        return obj.writeAt(out);
        //return out; //mit dieser Zeile wuerde es gehen
    }
    }
    
    #include <iostream>
    #include <string>
    using namespace std;
    
    int main()
    {
        cout<<foo1::C<string>();
        //cout<<foo2::C<string>();
    }
    

    Unterschied:
    foo1 hat eine Methode printAt, die den operator<< implementiert.

    Beide Zusammen geht auch nicht, dann gibts bei beiden einen 'operator<< not found'

    Ich poste mal die ganze Fehlermeldung:

    error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'foo1::C<StringT>' (or there is no acceptable conversion)
    with
    [
    StringT=std::string
    ]

    Der Code ansich sollte korrekt sein. Denn ICC7 kompiliert es. Den gesamten Code (aus dem ich dieses minimal Beispiel gemacht habe) kompiliert mit VC++6 (!!), ICC7, gcc3.2, BCB5, DMC++, CW8 und Comeau fehlerfrei.

    Ich hoffe ihr findet einen Workaround! Denn ich will den opeator<< eigentlich nicht als friend haben 😞

    Danke!!



  • template<class StringT> 
    inline std::basic_ostream<typename StringT::value_type, typename StringT::traits_type>& operator<<(std::basic_ostream<typename StringT::value_type, typename StringT::traits_type>& out, C<StringT> const& obj) 
    { 
        return obj.writeAt(out); 
        //return out; //mit dieser Zeile wuerde es gehen 
    }
    

    Klär mich mal kurz auf: sowohl obj, als auch out sind Parameter. Weshalb als Teil der Klasse (oder Freund)?

    Und wieso kann ein als Methode implementierter operator << zwei Argumente empfangen?



  • Helium schrieb:

    Klär mich mal kurz auf: sowohl obj, als auch out sind Parameter. Weshalb als Teil der Klasse (oder Freund)?

    Und wieso kann ein als Methode implementierter operator << zwei Argumente empfangen?

    der operator<< ist ja kein Teil der Klasse.
    Der op<< soll nur writeAt() aufrufen, welches in Wirklichkeit virtuell ist.

    Ich verstehe deine Frage nicht ganz.

    Der operator<< ist eine Funktion, genau so wie es sein soll. Oder habe ich nen bloeden Syntaxfehler drinnen?



  • Nachtrag:
    der VC++6 bringt den selben operator<< not found Fehler.

    Ich hatte vorher irrtuemlich mit dem Intel Compiler kompiliert.



  • Jetzt will er keine der Beiden Versionen mehr... Sehr komisch.

    Hat jemand eine Idee?



  • Ups. Ich hatte grade in 'nem anderen Forum was gelesen. Und irgendwie die beiden Probleme jetzt durcheinander gebracht. Sorry, vergiss einfach, was ich geschrieben hab.

    OK, muss ich selbst zu Hause mal testen.



  • wenn du anstatt den String zu templatisieren, den string::value_type templatisierst (gibts das wort eigentlich 😃 ) , funktioniert es:

    namespace foo1 
    { 
    template<class Ch>
    class C 
    { 
    public: 
        std::basic_ostream<Ch,std::char_traits<Ch> >&
    		writeAt(std::basic_ostream<Ch, std::char_traits<Ch> >& stream) const 
        { 
    		stream<<"xyz"<<endl;
            return stream; 
        } 
    }; 
    
    template<class Ch>
    inline std::basic_ostream<Ch, std::char_traits<Ch> >& 
    operator<<(std::basic_ostream<Ch, std::char_traits<Ch> >& out, C<Ch> const& obj) 
    { 
        return obj.writeAt(out); 
        //return out; //mit dieser Zeile wuerde es gehen 
    } 
    }
    

    weiß natürlich nicht ob das akzeptabel für dich ist..

    mfg shady



  • Leider ist das nicht moeglich 😞
    Denn es geht hauptsaechlich um die Implementierung von StringT und weniger um den char-Typen

    Aber danke, dass du dir die Muehe machst mir zu helfen.



  • Schade. Einen anderen Workaround seh ich jetzt leider nicht.
    Außer Microsoft zu boykotieren;)

    cu



  • Hallo Shade,
    das Problem ist nicht, dass er den operator nicht finden, sondern dass er den Typ von StringT nicht automatisch herleiten kann (das gilt zumindest für den VC 6.0).

    Das Problem lässt sich auf folgendes Minimalbeispiel reduzieren:

    #include <iostream> 
    #include <string> 
    using namespace std; 
    
    template<class T>
    void func(basic_ostream<typename T::value_type, typename T::traits_type>& o, const T& s)
    {
    
    }
    
    int main()
    {
    	func(cout, string());
    }
    

    Die abhängigen Namen T::value_type und T::traits_type verwirren ihn hier so sehr, dass er T nicht herleiten kann.

    Interessant wird's, wenn du die Parameter vertauschst:

    template<class T>
    void func(const T& s, basic_ostream<typename T::value_type, typename T::traits_type>& o)
    {
    
    }
    

    In diesem Fall hält er T für mehrdeutig (string oder char). Wie er auf diese Idee kommt ist mir unklar. char ist eindeutig kein gültiger Kandidat (es sei denn irgendjemand hat heimlich dem Typ char ein internes typedef spendiert). Scheinbar wertet er T::value_type aus und nimmt das Ergebnis dann als mögliches T.

    Ein Workaround fällt mir jetzt auf die schnelle nicht ein. Da es sich auch noch um einen Operator handelt, sind die Möglichkeiten wohl auch sehr limitiert (viele Tricks erfordern weitre Dummy-Parameter). Sieht auf jeden Fall nach einer Herausforderung aus. Leider habe ich im Moment nicht die Zeit mich dieser anzunehmen.

    Das einzige was ich dir empfehlen kann, ist eine ausführliche Suche in den boost-Quellen bzw. auf der boost-Mailinglist (vielleicht irgendwas mit Dependent typename oder eine Suche nach der Fehlernummer C2784).

    Ich weiß, alles nicht sehr hilfreich, was ich hier schreibe.



  • HumeSikkins schrieb:

    Das einzige was ich dir empfehlen kann, ist eine ausführliche Suche in den boost-Quellen bzw. auf der boost-Mailinglist (vielleicht irgendwas mit Dependent typename oder eine Suche nach der Fehlernummer C2784).

    Thx, aber boost habe ich schon durchsucht, werde aber wohl intensiver suchen...

    Ich weiß, alles nicht sehr hilfreich, was ich hier schreibe.

    Oh doch! Du hast mir wenigstens sagen koennen woran er scheitert.
    Das ist schonmal eine Hilfe.

    Danke 👍



  • Das bringt mich auf eine billig Loesung:

    template<class StringT>
    class C
    {
    public:
        std::basic_ostream<typename StringT::value_type, typename StringT::traits_type>& writeAt(std::basic_ostream<typename StringT::value_type, typename StringT::traits_type>& stream) const
        {
            return stream;
        }
    };
    
    template<typename CharT, class TraitsT, class StringT>
    inline std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& out, C<StringT> const& obj)
    {
        return obj.writeAt(out);
    }
    

    Was haltet ihr davon?
    Wenn StringT::value_type und CharT nicht gleich sind, dann ist die Fehlermeldung zwar n bisschen doof, aber dafuer funktioniert es.

    Ich werde aber trotzdem mal gruendlich boost durchsuchen.



  • Wenn StringT::value_type und CharT nicht gleich sind, dann ist die Fehlermeldung zwar n bisschen doof

    Da kannst du doch ein schönes static_assert für nehmen.


Anmelden zum Antworten