Objekte von Class Template in Container?



  • Hallo,

    ich hatte einige Monate Pause gemacht (aber immer hier mitgelesen). Jedenfalls hänge ich jetzt an einer Sache, wo ich nicht weiterkomme.

    template<typename T>
    struct Value
    {
        Value( const std::string& n, T val ) : name( n ), value( val ) {}
    
        void printValue() const
        {
            std::cout << name << ": " << value << '\n';
        }
        std::string name;
        T value;
    };
    

    Wie bekomme ich Objekte davon in einen Container? Welche Art von Container ist glaube ich zweitrangig, weil es ein tieferes Problem gibt. Ähnliche Threads gibt es auch bei stackoverflow, dort gibt es verschiedene Lösungsansätze, ich würde das aber lieber hier erklärt bekommen. Danke.



  • Ähnliche Threads gibt es auch bei stackoverflow, dort gibt es verschiedene Lösungsansätze, ich würde das aber lieber hier erklärt bekommen.

    Das motiviert natürlich ungemein dir zu antworten. Magst du uns verraten was genau dir an den Antworten die du auf SO gefunden hast nicht zusagt, so dass du es hier nochmal erklärt bekommen magst?

    Davon abgesehen kann ich dir deine Frage natürlich beantworten: Objekte "davon" gibt es nicht, da "das" ein Klassentemplate ist, und ein Klassentemplate ist nunmal keine Klasse. Demnach kann man auch keine Objekte von einem Klassentemplate erzeugen. Und was man nicht erzeugen kann, kann man auch nicht in einen Container stecken.

    Danke.

    Bitte.



  • Naja, du musst dem Compiler erstmal Argumente liefern, aus dem er dann eine Klasse generieren kann. Wenn die Klasse dann steht, kannst du eine Instanz erzeugen. Sehe jetzt das Problem nicht wirklich, weiß auch nicht, woran du da jetzt genau hängst?

    z.B.

    vector< Value<int> > vec{ Value{"Hans",12}, Value{"Oskar",3} };
    


  • Dieser Beitrag wurde gelöscht!


  • Wenn es nur lauter ints wären, brauchte ich ja kein template.

       Value<int> v1( "int", 5 );
       Value<std::size_t> v2( "size_t", 3 );
       Value<double> v3( "double", 0.5 );
    


  • Davon abgesehen daß doubles keine integralen Werte sind ... was hat das jetzt mit der ursprünglitschen Frage zu tun?



  • Das ist für mich wieder so ein klassischer Fall, wo Du lieber erklären solltest, was Du zu erreichen versuchst. Wenn Du mit deinem Ansatz Values verschiedener Typen im selben Container willst, müsstest Du eine abstrakte Basisklasse erstellen, und Zeiger darauf im Container speichern. Davon würde ich allerdings abraten:

    // Pseudocode, kompiliert evtl. nicht.
    struct BaseValue {
        virtual void printValue() const = 0;
    };
    
    template<class T>
    struct Value : public BaseValue {
        std::string name;
        T value;
        virtual void printValue() const override {
            std::cout << name << ": " << value << '\n';
        }
    };
    
    std::vector<std::unique_ptr<BaseValue>> v;
    v.emplace_back(std::make_unique<Value<int>>("foo", 5));
    

    Wie gesagt, das ist eine ziemlich schlechte Lösung für so ein Problem IMO. Daher lieber std::variant oder wenn's sein muss std::any. Wenn sich die Anzahl verwendeter Typen begrenzen lässt, dann würde ich std::variant wählen.



  • Ich will an meiner Fehlerbehandlung arbeiten und eine eigene Exceptionklasse schreiben. Um mich nicht zu verschlechtern, will ich alle Bezeichner und Werte ausgeben können, die mit einem Fehler zusammenhängen.

    Dafür will ich im dortigen Konstruktor beliebig viele Werte übergeben können. Dies wollte ich mit einer initializer_list machen, aber wie bekomme ich die Werte dort rein?



  • Was. Willst. Du. Konkret?

    (Nein. Nochmal. Nicht *wie* sondern *was*?)



  • Fehlerbehandlung. Exceptionklasse. Werte im Fehlerfall ausgeben können.
    Urgerüst als billiges Beispiel:

    class Exception
    {
    public:
    
        Exception( const std::string& name ) : error_name( name ) {}
    
        void printErrorName() const
        {
            std::cerr << error_name;
        }
    
    private:
    
        std::string error_name;
    };
    
    void getV( const std::vector<int>& vec, const std::size_t i )
    {
        if ( i >= 0 && i < vec.size() )
        {
            std::cout << vec[ i ];
        }
        else
        {
            throw Exception( "range_error" );
        }
    }
    
    int main()
    {
        std::vector<int> vec = { 1, 2, 3 };
    
        try
        {
            getV( vec, 5 );
        }
        catch( const Exception& e )
        {
            e.printErrorName();
        }
    }
    
    

    Nun möchte ich aber noch vec.size() und i ausgeben können.



  • #include <sstream>
    #include <stdexcept>
    #include <vector>
    #include <iostream>
    
    void getV(std::vector<int> const &vec, std::size_t i)
    {
        if (0 < i && i < vec.size())
        {
            std::cout << vec[i];
        }
        else {
            std::stringstream ss;
            ss << "range error in getV(), vec.size(): " << vec.size() << ", i: " << i;
            throw std::range_error{ ss.str() };
        }
    }
    

    Aber warum muss i größer 0 sein?
    Warum überhaupt getV() und nicht vec.at(), das wirft freiwillig.



  • Schreib- oder Denkfehler.

    Ich dachte, es wäre besser, wenn ich im else-Fall keinen Roman schreiben muss, sondern wie "range_error" alle Werte dem Konstruktor übergebe.



  • @Swordfish sagte in Objekte von Class Template in Container?:

    Warum überhaupt getV() und nicht vec.at(), das wirft freiwillig.

    Weil es ein blödes kompilierbares Minimalbeispiel ist!



  • Mach es einfach nicht komplizierter als es ist.



  • Dann habe ich keine Verbesserung gegenüber meinen std::cerr Ausgaben.



  • Für sowas gibt es Debugger --- Dein Programm stürtzt ab, dann machst Du breakpoints hin und kannst sehen, welchen Wert der Index hat, wie groß der vector ist usw. Was deine ursprüngliche Beschreibung angeht

    will ich alle Bezeichner und Werte ausgeben können, die mit einem Fehler zusammenhängen.

    Dafür will ich im dortigen Konstruktor beliebig viele Werte übergeben können.

    Das klingt wie in diesem Thread: C++ Komfort-Exception

    P.S.: Ich habe das Gefühl, dass deine ursprüngliche Fragestellung überhaupt nichts mit dem eigentlichen Problem zu tun hat... Also wie gesagt am Besten immer direkt beschreiben was Du lösen willst, und nicht wie Dein Lösungsansatz für ein unbekanntes Problem aussieht.



  • @zeropage
    Mir war schon klar was du erreichen wolltest, nämlich beliebige Spezialisierungen deines Templates in ein und den selben Container zu stopfen. Nur das geht halt nicht.

    Wenn du möchtest, kannst du eine gemeinsame, polymorphe Basisklasse machen, von der alle Spezialisierungen deines Templates ableiten. Die kann dann eine virtuelle Funktion haben (oder auch mehrere). Dann kannst du sowas wie einen vector<unique_ptr<Basisklasse>> machen.

    Jetzt sind wir vermutlich wieder genau bei der Antwort die du schon gefunden hast und wo dir irgendwas dran nicht passt.

    Und nu?


    Davon abgesehen: Wieso packst du nicht gleich den ganzen Fehlertext den printErrorXxx() ausgeben soll in die Exception?
    Oder, die "saubere" Lösung (die aber oft Overkill ist): du machst für jeden Fehlertyp eine eigenen, von Exception abgeleitete Klasse. Die kann dann printErrorXxx so implementieren wie sie möchte - vorausgesetzt du machst printErrorXxx virtual.



  • @zeropage sagte in Objekte von Class Template in Container?:

    Dann habe ich keine Verbesserung gegenüber meinen std::cerr Ausgaben.

    Dann nimmst eben ein Variadic Template?

    #include <sstream>
    #include <stdexcept>
    #include <vector>
    #include <iostream>
    
    class my_hyper_cool_range_error
    {
    	std::string msg;
    
    public:
    	template<typename T, typename... Ts>
    	my_hyper_cool_range_error(T value, Ts... values)
    	{
    		std::stringstream ss;
    		ss << value;
    		(ss << ... << values);
    		msg = ss.str();
    	}
    
    	std::string const& what() const { return msg; }
    };
    
    void getV(std::vector<int> const &vec, std::size_t i)
    {
    	if (0 < i && i < vec.size()) {
    		std::cout << vec[i];
    	}
    	else {
    		throw my_hyper_cool_range_error{ "range error in getV(), vec.size(): ", vec.size(), ", i: ", i };
    	}
    }
    
    int main()
    {
    	std::vector<int> vec = { 1, 2, 3 };
    
    	try {
    		getV(vec, 5);
    	}
    	catch (my_hyper_cool_range_error &e) {
    		std::cerr << e.what() << "\n\n";
    	}
    }
    


  • Danke. Wenn ich die Arschloch-Bezeichner überlesen kann, schaue ich es mir mal an. Irgendwann oder so. Ist nicht mehr so wichtig jetzt.



  • @zeropage sagte in Objekte von Class Template in Container?:

    Arschloch-Bezeichner

    ?


Anmelden zum Antworten