das compiliert nicht mit comeau



  • Der folgende Code compiliert nicht mit dem Comeau-Compiler
    http://www.comeaucomputing.com/tryitout/

    Die Fehlermeldung ist:

    "ComeauTest.c", line 11: error: no operator "<<" matches these operands
    operand types are: std::stringstream << const akt::Content
    strm << Value;
    ^
    detected during instantiation of "const std::string ToString(const T
    &) [with T=akt::Content]"

    #include <string>
    #include <sstream>
    
     template <typename T>
     const std::string
    ToString(const T& Value)
    {
      std::stringstream strm;
    
      strm << Value;
      return strm.str().c_str();
    }
    
    namespace akt{
       class
      Content;
    }
    
     std::ostream&
    operator<<( std::ostream& os, const akt::Content&);
    
     enum
    Manips{ e};
    
     class
    CStream;
    
     CStream&
    operator<< (CStream& Strm, const Manips& Manip);
    
     void
    foo( )
    {
       akt::Content*
      pCont(NULL);
    
      ToString( *pCont); 
    }
    

    sobald ich Content aus dem Namensraum akt raustu, funzts.

    Das ist die kürzeste Version die ich durch zusammenkopieren aus diversen Headern etc. zusammenkriege (und nicht funzt).
    Irgendwas passt da mit den Namensräumen nicht. Obige Version geht z.B. mit VC++, aber im orginal ist noch ein andrer Namespace dabei und ich krieg den gleichen Fehler wie vom Comeau hiermit.



  • In C++ muss man eine Funktion vor ihrer Verwendung deklarieren. Also

    namespace akt{
       class
      Content;
    }
    
     std::ostream&
    operator<<( std::ostream& os, const akt::Content&);
    

    for ToString stellen und es sollte kompilieren.



  • hm, hast recht. Aber mach mich nicht schwach:

    in meinem Projekt sind das eigene Header.
    Muss ich den Header mit Content einbinden BEVOR ich den Header mit der Template-Funktion ToString einbinde?

    Der Template-Code wird doch erst übersetzt, wenn er typisiert ist. Und das passiert doch erst in der Funktion foo!?

    Das is in meiner Realität unmöglich, weil der ToString-Header in zig anderen Headern eingebunden wird und der Content-Header nur im cpp-File eines Moduls (und der Content-Header am anfang auch noch selber ToString includiert)

    //Content.h
    #include "ToString.h"
    //... definition der Klasse Content
    

    Und warum funzts, wenn das Content nicht im Namensraum ist ?????



  • std::ostream& operator<<( std::ostream& os, const akt::Content&);

    Gehört in den Namespace akt, da er ein Teil der Klasse Content ist. Sonst wird dir das Koenig-Lookup nen Streich spielen.



  • hm, hm, auch Du hast recht 😉

    Das ist mir auch die liebere Lösung. Nur, was ich nicht verstehe:

    Wenn der op<< im Namensraum akt ist, woher weiß der Compiler dann in foo(), dass er akt::op<< nehmen muss, wo doch dieser Namenraum nicht offen ist.
    Das ist der Grund, warum ich die Ops immer global außerhalb des Namensraums mache. Ist das eine Fehlannahme meinerseits?



  • Koenig-Lookup sagt (vereinfacht):
    Bei der Suche nach einer passenden Funktion, durchsuche nicht nur deinen gerade sichtbaren Namensraum, sondern auch den Namensraum, aus dem das entsprechende Argument stammte. In diesem Fall akt:: . Erst wenn garkeine passende Funktion gefunden wurde (egal welche Signatur), wird der anonyme Namensraum :: durchsucht.

    Das Thema wird in Sutters "Exceptional C++" recht ausführlich behandelt. Auf jeden Fall ein Kauftipp, das Buch.



  • ok: http://fara.cs.uni-potsdam.de/~kaufmann/?page=GenCppFaqs&faq=Koenig#Answ

    Eins kann ich Euch sagen:
    Bestehende Projekte von einem Compiler auf einen anderen zu bringen ist echt der Hammer, obwohl ich immer geglaubt hab, Standard-C++ zu programmieren.

    Vielen Dank!!!



  • hm, trotzdem noch ne Frage:

    Bei meinem obigen Beispiel (und bei meinem nächsten Problem 😉 ) gibts ja die passende Funktion im globalen Namensraum. Und die nimmt er nicht, weil er die Namensräume durchsucht und was findet. Das compiliert er aber nicht, weil die Signatur nicht passt. Was mach ich jetzt? Die Funktion, aus dem globalen Namensraum in jedem einzelnen Namensraum nochmal aufnehmen ...?

    Vielleicht sollt ich ins Bett gehn!



  • Wenn ich Euch nochmal mit einem Beispiel nerven darf 😃

    #include <iostream>
    #include <utility>
    
    namespace std
    {
      template <typename Tfirst, typename Tsecond>
    
      std::ostream&
      operator <<(std::ostream& ostrm, const std::pair<Tfirst, Tsecond>& pair )
      {
    
        ostrm << pair.first<<"\t"<<pair.second;
    
        return ostrm;
      }
    }
    
     namespace
    akt
    { 
       typedef unsigned short
      InnerPriority;
    
       typedef unsigned short
      OuterPriority;
    
       class
      Priority
      {
      public:
    
         void
        serialize( std::ostream& os)const;
    
      private:
    
         std::pair< OuterPriority,
                    InnerPriority>
        value_;
      };
    
      std::ostream& operator<<( std::ostream&         os,
                               const akt::Priority& priority);
    
    }
    
    namespace akt
    {   
       void
       Priority::
      serialize( std::ostream& os)const
      {
        os << value_;
      }
    }
    

    Ich hab also einmal nen streaming-Op für std::pair. Dann hab ich ne Klasse mit ner Serialize-Funktion, die selbigen nutzen möchte. Jetzt gibts aber im Namensraum der Klasse noch nen anderen überladenen Streaming-Op.
    Bis jetzt hatte ich den std::pair-streamingOp im globalen Namensraum (mit denselben Problemen wie oben). Jetzt hab ich ihn in std rein und es funzt.
    Das ist klar und versteh ich.

    Nur: es gefällt mir nicht, dass ich den Namenraum einer Bibliothek, die nicht vom mir ist, aufmachen und da was reinnehmen muss. Der Namensraum std geht mich doch als nicht-STL-Entwickler garnix an. Find ich nicht schön.

    Macht man das wirklich so?



  • Ein klares nein. Es ist verboten, den Namensraum std:: anzufassen.


Anmelden zum Antworten