template-spezialisierung auf std::string schlägt fehl



  • hallo,

    ich habe um nochmal meine kenntnisse aufzufrischen eine test-klasse geschrieben, in die ich einige (sinnlose) spielereien eingebaut habe, eben nur, um wieder etwas firmer zu werden.

    klappt soweit auch alles ganz gut. allerdings habe ich das problem, dass, wenn ich meine template-klasse auf std::string spezialisiere, keinerlei ausgabe erfolgt, was aber eigentlich gewollt ist.

    außerdem habe ich noch einige fragen in form von kommentaren im quellcode eingebracht.

    wäre echt dankbar, wenn mal jemand drüberschauen würde 🙂

    #include <iostream>  
    #include <fstream>
    #include <string>
    #include <exception>
    #include <typeinfo>
    
    using namespace std; 
    
    //////////////////////////////////
    //////////	class Test	//////////
    //////////////////////////////////
    template <class T>
    class Test 
    { 
    public: 
        Test(const Test& right)
        {
    		m_Value = right.getValue();
    
    		if(m_Value != right.getValue())
    			throw ("ERROR");
        } 
    
        Test()
    	{
    		if(typeid(T).name()==typeid(string).name()) //falls Test auf std::string spezialisiert wurde->throw exception (kleine spielerei =) )
    			throw ("DIFFERENT TYPE THAN STD::STRING IS REQUIRED.");
    	}
    
        Test(T value) : m_Value(value) {}
        ~Test() {}
    
        T getValue() const { return m_Value; } // <---würde gerne eine referenz auf m_Value zurückgeben, um weiter unten im überl. >> operator den zugriff auf m_Value durch den aufruf von getValue() zu ersetzen und somit die deklaration v. operator >> als friend von Test überflüssig zu machen. außerdem wäre eine referenz bei der spezialisierung v. Test auf std::string sinnvoll.
    
        const Test& operator=(const Test& right)
        {
              m_Value = right.getValue();
    
              return *this;
        }
    
    	void save(string name)
    	{
    		ofstream DATEI(name.c_str());
    
    		if(DATEI)
    			DATEI << m_Value << endl;
    
    		else throw("CAN'T SAVE DATA. CAN'T CREATE FILE.");
    	}
    
    	void load(string name)
    	{
    		ifstream DATEI(name.c_str());
    
    		if(DATEI)
    			DATEI >> m_Value;
    
    		else throw("CAN'T LOAD DATA. FILE DAMAGED OR DOES NOT EXIST.");
    	}
    
        template <class T>
        friend istream& operator>>(istream& in, Test<T>& testit);
    
    private: 
        T m_Value;
    }; 
    //////////////////////////////////
    ///////	END class Test END	//////
    //////////////////////////////////
    
    template <class T>
    ostream& operator<<(ostream& out, const Test<T>& testit)
    { 
    	out << testit.getValue(); 
    
    	return out; 
    } 
    
    //ermöglicht schreiben von m_Value in datei, falls m_Value vom typ std::string ist (benötigt für Test::save() )    
    ostream& operator<<(ostream& out, const string& testit)
    {
    	out << testit;
    
    	return out;
    }
    
    template <class T>
    istream& operator>>(istream& in, Test<T>& testit)
    { 
    	in >> testit.m_Value; 
    
    	return in; 
    } 
    
    //ermöglicht laden von datei in m_Value, falls m_Value vom typ std::string ist (benötigt für Test::load() )    
    istream& operator>>(istream& in, string& testit)
    {
    	in >> testit;
    
    	return in;
    }
    
    int main() 
    { 
    	try
    	{
    		Test<string> test("tada");
    
    		//cin >> test2; klappt nicht. warum? (muss irgendwie mit der tatsache, dass der überl. op. >> als friend deklariert wurde. 
    
    		cout << test << endl; 
    
    		test.save("TEST.txt");
    		test.load("TEST.txt");
    
    		cout << test << endl;
    	}
    
    	catch(const char* a)
    	{
    		cout << a << endl;
    	}
    
    	return 0; 
    }
    


  • Was meldet der Compiler denn?



  • Kräuterkundestudent schrieb:

    Test()
    	{
    		if(typeid(T).name()==typeid(string).name()) //falls Test auf std::string spezialisiert wurde->throw exception (kleine spielerei =) )
    			throw ("DIFFERENT TYPE THAN STD::STRING IS REQUIRED.");
    	}
    
    • Lass mal die .name() weg.
    • Machs doch ganz anders:
    template<> class Test<std::string> {
    Test(); // privater Konstruktor, haha!
    ~Test(); // Destruktor ebenso.
    }
    


  • So läuft's ohne Probleme:

    #include <iostream>   
    #include <fstream> 
    #include <string> 
    #include <exception> 
    #include <typeinfo> 
    
    using namespace std;  
    
    ////////////////////////////////// 
    //////////    class Test    ////////// 
    ////////////////////////////////// 
    template <class T> 
    class Test  
    {  
    public:  
        Test(const Test& right) 
        { 
            m_Value = right.getValue(); 
    
            if(m_Value != right.getValue()) 
                throw ("ERROR"); 
        }  
    
        Test() 
        { 
            if(typeid(T).name()==typeid(string).name()) //falls Test auf std::string spezialisiert wurde->throw exception (kleine spielerei =) ) 
                throw ("DIFFERENT TYPE THAN STD::STRING IS REQUIRED."); 
        } 
    
        Test(T value) : m_Value(value) {} 
        ~Test() {} 
    
        T getValue() const { return m_Value; } // <---würde gerne eine referenz auf m_Value zurückgeben, um weiter unten im überl. >> operator den zugriff auf m_Value durch den aufruf von getValue() zu ersetzen und somit die deklaration v. operator >> als friend von Test überflüssig zu machen. außerdem wäre eine referenz bei der spezialisierung v. Test auf std::string sinnvoll. 
    
        const Test& operator=(const Test& right) 
        { 
              m_Value = right.getValue(); 
    
              return *this; 
        } 
    
        void save(string name) 
        { 
            ofstream DATEI(name.c_str()); 
    
            if(DATEI) 
                DATEI << m_Value << endl; 
    
            else throw("CAN'T SAVE DATA. CAN'T CREATE FILE."); 
        } 
    
        void load(string name) 
        { 
            ifstream DATEI(name.c_str()); 
    
            if(DATEI) 
                DATEI >> m_Value; 
    
            else throw("CAN'T LOAD DATA. FILE DAMAGED OR DOES NOT EXIST."); 
        } 
    
        template <class T> 
        friend istream& operator>>(istream& in, Test<T>& testit); 
    
    private:  
        T m_Value; 
    };  
    ////////////////////////////////// 
    ///////    END class Test END    ////// 
    ////////////////////////////////// 
    
    template <class T> 
    ostream& operator<<(ostream& out, const Test<T>& testit) 
    {  
        out << testit.getValue();  
    
        return out;  
    }  
    
    template <class T> 
    istream& operator>>(istream& in, Test<T>& testit) 
    {  
        in >> testit.m_Value;  
    
        return in;  
    }    
    
    int main()  
    {  
        try 
        { 
            Test<string> test("tada"); 
    
            //cin >> test2; klappt nicht. warum? (muss irgendwie mit der tatsache, dass der überl. op. >> als friend deklariert wurde.  
    
            cout << test << endl;  
    
            test.save("TEST.txt"); 
            test.load("TEST.txt"); 
    
            cout << test << endl; 
        } 
    
        catch(const char* a) 
        { 
            cout << a << endl; 
        } 
    
        return 0;  
    }
    


  • Kräuterkundestudent schrieb:

    template <class T>
        friend istream& operator>>(istream& in, Test<T>& testit);
    

    versuch mal lieber friend istream &operator>>(istream &in, Test &testit);



  • MaSTaH schrieb:

    Was meldet der Compiler denn?

    der compiler meldet gar nichts. das ist das problem (microsoft visual c++).

    @ Mr. N:

    gut, name() hab ich jetzt jeweils weggelassen. das ändert aber erstmal nichts.

    • Machs doch ganz anders:
    template<> class Test<std::string> {
    Test(); // privater Konstruktor, haha!
    ~Test(); // Destruktor ebenso.
    }
    

    ich verstehe nicht ganz, was das bezwecken soll. könntest du ein klein wenig mehr code dazu posten? im grunde ist das doch equivalent zu

    class Test{
    //...
    string m_Value;
    };
    

    oder?

    edit: so schnell, wie ihr hier postet aknn ich ja gar nicht mithalten 😉
    die überarbeitete version von mastah funktioniert anscheinend. ich werde mir das mal genauer anschauen. hier schonmal vielen dank 🙂



  • @Kräuterkundestudent:
    das was ich als ganz anders bezeichnet hab is eine explizite spezialisierung ... wenn du nun schreibst Test<string> dann nimmt er die damit beschriebene klasse.
    und wenn nun diese klasse private konstruktoren hat, dann meldet bereits der compiler, dass Test<string> verboten ist.
    und jetzt zeig ich dir eine partielle spezialisierung *hrhr*:

    template<class T, class Ch, class A> class Test<std::basic_string<T, Ch, A> > {
      Test();
    }
    

    jetzt geht das auch mit wstring.
    übrigens erfordert eine spezialisierung, dass die klasse (oder auch die funktion als template bekannt ist). also erst das template<class T> class Test {....};



  • @Mastah: es wäre _sehr_ hilfreich wenn du bitte die von dir vorgenommenen änderungen markieren würdest, sonst erkennen das meine blinden augen nich 🙂



  • gut, dass ich die operatoren << bzw. >> für string extra überladen habe, war unnötig, allerdings wundere ich mich doch, warum es da zu solchen komplikationen kommt. war der code etwa fehlerhaft?

    @ Mr. N:
    das mit der expl. spezialisierung wär dann auch wieder in mein hirn gebrannt 😉 auf den priv. kontruktor / destruktor verzichte ich, da ich mal mit typeid rumspielen wollte 🤡 außerdem bin ich geil auf die exception 😃

    würdet ihr mir noch kurz auf die sprünge helfen, warum die eingabe per cin nicht funktioniert (siehe kommentare im code)?



  • @Kräüterkundestudent:
    Zu dem cin>> hab ich dir bereits gesagt, was du versuchen sollst damits besser geht. Btw. wäre sowas eh schöner (imho):

    template<class T> class Test {
      void read(istream &is);
    };
    
    template<class T> istream &operator>>(istream &is, Test<T> &x) {
      x.read(is);
      return is;
    }
    


  • danke, ich glaube so krieg ichs hin 🙂

    edit: ich hab es jetzt, wie von Mr. N vorgeschlagen gelöst.

    einmal die friend-deklaration gelöscht und
    stattdessen eine read-methode ergänzt:

    void read(istream& in)
    	{
    		in >> m_Value;
    	}
    //...
    
    template <class T>  
    istream& operator>>(istream& in, Test<T>& testit)  
    {   
    	testit.read(in);   
    
        return in;   
    }
    

    nochmal danke.



  • nur mal aus purer neugier:
    mach die read funktion mal privat und schreibe in die klasse Test:

    friend istream &operator>>(istream &, Test &);
    

    wenns dann geht weißt du auch wies geht, wenn nicht, dann weiß ich, wies nicht geht 🤡



  • du weißt jetzt, wies nicht geht 😉

    noch eine letzte sache. ich hab mir gestern schon einige zeit den kopf zerbrochen, wieso ich die methode

    getValue()
    

    nicht als

    T& getValue() { return m_Value;}
    

    deklarieren kann, also eine referenz zurückgeben lasse.

    dann kriege ich gleich wieder einen fehler der art:

    error C2662: 'getValue' : this-Zeiger kann nicht von 'const class Test<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >' in 'class Test<
    class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > &' konvertiert werden



  • überladen du musst (manche leute würden das polymorphie nennen aber egal):

    T const &getValue() const;
    T &getValue();
    

    dass du das tun musst kann man übrigens aus der fehlermeldung erkennen



  • gut, das klappt...und ich weiß sogar, warum 🤡

    danke!



  • Mr. N schrieb:

    das was ich als ganz anders bezeichnet hab is eine explizite spezialisierung ... wenn du nun schreibst Test<string> dann nimmt er die damit beschriebene klasse.
    und wenn nun diese klasse private konstruktoren hat, dann meldet bereits der compiler, dass Test<string> verboten ist.

    er meldet bestimt sowas wie "class Test<basic_string<T ..>>> ctor ist nicht zugreifbar.. xyz " das kapiert aber kein mensch, schöner fände ich ein static assert oder änliches (habe schon zu lange kein c++ gemacht boost::static_assert ist doch dafür geeignet?)



  • Dimah schrieb:

    habe schon zu lange kein c++ gemacht

    darf ich neugierig sein und fragen was dann bzw. wie das gemeint ist?



  • hallo nochmal 🙂

    ich wollte meine übung jetzt so zu ende bringen, indem ich einen std::vector auf meine klasse Test<T> (die inzwischen etwas erweitert wurde (siehe quellcode)) spezialisiere. seltsamerweise tauchen genau dann, wenn ich in das programm den vector einbaue fehler auf.

    der quellcode ist etwas länger als die meisten anderen hier geposteten. ich würde es also sehr schätzen, wenn sich diesem einer annehmen würde. da ich die fehlermeldungen überhaupt nicht zuordnen kann, fällt es mir auch schwer das programm auf das wesentliche zu reduzieren 😕 . daher sei gesagt, dass mir der debugger meldet, dass der fehler vermutlich irgendwo beim konstruktor von Test<T>, der einen wert vom typ T entgegennimmt, auftaucht.

    die fehlermeldungen bzw. warnungen lauten:

    warning C4786: 'std::reverse_iterator<Test<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > const *,Test<std::basic_string<char,std::char_traits<char>,std
    ::allocator<char> > >,Test<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > const &,Test<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > const *,int>' : Bezeichner wurde auf '255' Zeichen in den Debug-Inf
    ormationen reduziert

    warning C4786: 'std::reverse_iterator<Test<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > *,Test<std::basic_string<char,std::char_traits<char>,std::allo
    cator<char> > >,Test<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > &,Test<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > *,int>' : Bezeichner wurde auf '255' Zeichen in den Debug-Informationen reduziert

    error C2059: Syntaxfehler : '<' Bei der Kompilierung der Member-Funktion '__thiscall Test<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >::Test<class std::basi
    c_string<char,struct std::char_traits<char>,class std::allocator<char> > >(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)' der Klassenvorlage

    #include <iostream>     
    #include <fstream>   
    #include <string>   
    #include <exception>   
    #include <typeinfo> 
    #include <vector>   
    
    using namespace std; 
    
    /////////////////////////////////////   
    ////    class memberOfTest<T>    ////  
    ///////////////////////////////////// 
    template <class T> 
    class memberOfTest 
    { 
        public: 
            memberOfTest() { m_Pointer = new T;} 
    
            memberOfTest(T value); 
    
            memberOfTest(const memberOfTest<T>& right); 
    
            const memberOfTest<T>& operator=(const memberOfTest<T>& right); 
    
            virtual ~memberOfTest() { delete m_Pointer;} 
    
            T* getPointer() const { return m_Pointer; } 
    
        private: 
            T* m_Pointer; 
    }; 
    
    //############################################################################### 
    template<class T> 
    memberOfTest<T>::memberOfTest(T value) 
    {  
        m_Pointer = new T; 
    
        *m_Pointer = value; 
    } 
    //############################################################################### 
    template<class T> 
    memberOfTest<T>::memberOfTest(const memberOfTest<T>& right) 
    { 
        m_Pointer = new T; 
        *m_Pointer = *(right.getPointer()); 
    } 
    //############################################################################### 
    template<class T> 
    const memberOfTest<T>& memberOfTest<T>::operator=(const memberOfTest<T>& right) 
    { 
        *m_Pointer = *(right.getPointer()); 
    
        return *this; 
    } 
    //############################################################################### 
    /////////////////////////////////////   
    //  END class memberOfTest<T> END  //   
    ///////////////////////////////////// 
    
    //############################################################################### 
    //############################################################################### 
    //############################################################################### 
    //############################################################################### 
    
    /////////////////////////////////////   
    ////////    class Test<T>    ////////  
    ///////////////////////////////////// 
    template<class T>   
    class Test : public memberOfTest<T>   
    {    
    public:    
        Test(const Test<T>& right);  
    
        Test(); 
    
        Test(T value) : m_Value(value), m_secValue(value), memberOfTest(value) {}   
    
        virtual ~Test() {} 
    
        T getValue() const { return m_Value; }   
    
        memberOfTest<T> getsecValue() const { return m_secValue; } 
    
        const Test<T>& operator=(const Test<T>& right);   
    
        void save(string name); 
    
        void load(string name); 
    
        void read(istream& in) { getline(in, m_Value); } 
    
    private:    
        T m_Value; 
        memberOfTest<T> m_secValue; 
    
    }; 
    
    //############################################################################### 
    template<class T> 
    Test<T>::Test(const Test<T>& right) : m_secValue(right.getsecValue()), memberOfTest<T>(right) 
    {   
        m_Value = right.getValue();   
    
        if((m_Value != right.getValue()) || (m_secValue != right.getsecValue()))   
            throw("ERROR. PROCESS INTERRUPTED.");   
    }  
    //###############################################################################  
    template<class T> 
    Test<T>::Test() : m_secValue<T>(), memberOfTest<T>()   
    {   
        if(typeid(T).name() == typeid(string).name()) 
            throw("DIFFERENT TYPE THAN STD::STRING IS REQUIRED.");   
    }   
    //############################################################################### 
    template<class T> 
    const Test<T>& Test<T>::operator=(const Test<T>& right)   
    {   
        if(this == &right) 
            return *this; 
    
        m_Value = right.getValue(); 
    
        m_secValue.operator=(right.getsecValue()); 
    
        memberOfTest<T>::operator=(right); 
    
        return *this;   
    }   
    //############################################################################### 
    template<class T> 
    void Test<T>::save(string name)   
    {   
        ofstream DATEI(name.c_str());   
    
        if(DATEI)   
            DATEI << m_Value << "\n" << *(m_secValue.getPointer()) << endl;   
    
        else throw("CAN'T SAVE DATA. CAN'T CREATE FILE. PROCESS INTERRUPTED.");   
    }   
    //############################################################################### 
    template<class T> 
    void Test<T>::load(string name)   
    {   
        ifstream DATEI(name.c_str());   
    
        if(DATEI) 
        { 
            string tempValue; 
            m_Value = ""; 
    
            for(; getline(DATEI, tempValue);) 
            {
    			m_Value += tempValue + "\n"; 
            } 
        } 
    
        else throw ("CAN'T LOAD DATA. FILE DAMAGED OR DOES NOT EXIST. PROCESS INTERRUPTED.");   
    }   
    //############################################################################### 
    /////////////////////////////////////  
    //////    END class Test END    /////   
    ///////////////////////////////////// 
    
    template <class T>   
    ostream& operator<<(ostream& out, const Test<T>& right)   
    {    
        out << right.getValue();    
    
        return out;    
    }    
    
    template <class T>   
    istream& operator>>(istream& in, Test<T>& right)   
    {    
        right.read(in);    
    
        return in;    
    }      
    
    int main()    
    {    
        try   
        { 
            vector<Test<string> > test; 
    
            test.push_back(Test<string>("tada")); 
    
            cin >> test[0];  
    
            cout << test[0] << endl;    
    
            test[0].save("d:\\TEST.txt");   
            test[0].load("d:\\TEST.txt");   
    
            cout << test[0] << endl; 
        }   
    
        catch(const char* a)   
        {   
            cout << a << endl;   
        }   
    
        return 0;    
    }
    

    schon jetzt danke 🙂 👍

    ps: wirklich mein letztes anliegen in dieser sache (versprochen ;)).



  • Test(T value) : m_Value(value), <T>m_secValue(value), memberOfTest<T>(value) {}
    

    Was soll da das <T>?



  • Mr. N schrieb:

    Test(T value) : m_Value(value), <T>m_secValue(value), memberOfTest<T>(value) {}
    

    Was soll da das <T>?

    naja, ich dachte, dass der konstruktor den template-typ extra wissen muss um das objekt zu erstellen.

    andererseits: lasse ich das <T> weg, so bleiben doch die fehlermeldungen 😞
    lasse ich beim m_secValue das <T> weg, so bekomme ich aufeinmal acht weitere fehlermeldungen, lasse ich das zweite <T> weg, so sind es zwei und lasse ich gar beide weg, so sind es auch zwei.


Anmelden zum Antworten