Template Argument( Jeder Container von einen bestimmen Type)



  • Hallo,
    Ich bin grade ein bisschen ratlos ich möchte gerne eine Funktion haben wo ich jeden Container also Vector, List etc. als Parameter nehmen kann aber nur wenn diese einen bestimmen Type beinhalten.
    z.B.

    std::vector<bestimmter type> vec;
    std::list<bestimmter type> list;
    etc.
    

    Meine Funktion sieht so aus:

    template<template <class> class ContainerT>
    	void funktion(std::istream& is, ContainerT<bestimmter type> cont);
    

    aber ich bekomme folgenden Fehler:

    Fehler	1	error C2784: "void funktion(std::istream &,ContainerT<bestimmter type>)": template-Argument für "ContainerT<bestimmter type>" konnte nicht von "std::vector<bestimmter type,std::allocator<_Ty>>" hergeleitet werden.	d:\...	12	1	Projekt 0.0.1
    
    	    2	IntelliSense: Keine Instanz von Funktionsvorlage "funktion" stimmt mit der Argumentliste überein.
                Argumenttypen sind: (std::istream, std::vector<bestimmter type, std::allocator<bestimmter type>>)	d:\...	12	2	SerienMgr 0.0.1
    

  • Mod

    STl-Container nehmen als zweites Template Argument i.d.R. noch einen Allokatortyp.

    template<template <class, class> class ContainerT,
             typename Allocator>
    void funktion(std::istream& is, ContainerT<bestimmter type, Allocator> cont);
    


  • Warum brauch man beim erstellen eines Containers den Allokatortyp nicht anzugeben???


  • Mod

    CraftPlorer schrieb:

    Warum brauch man beim erstellen eines Containers den Allokatortyp nicht anzugeben???

    Weil alle Container dafür ein Default Argument haben, nämlich std::allocator<T> .



  • Ok Danke dir.
    Ich glaub ich gib mir nochmal das Kapitel mit Templates.



  • Aber selbst die Variante mit Allocator ist nicht ganz schön, da ja eine Map auch ein Container ist und zwei Parameter hat.
    Ich würd da einfach irgendeinen Typen Container nehmen, an den Typparemter kommt man ja via value_type.

    Edit: Ach so, du willst nur einen bestimmten Typen haben. Dann ist das so schon am besten.


  • Mod

    template<typename ContainerT>
        void funktion(std::istream& is, ContainerT& cont)
    {
        typedef typename ContainerT::value_type T;
        static_assert( std::is_same<T, some_type>::value, "unzulässiger Containerelementtyp" );
        ...
    }
    
    // oder
    
    template<typename ContainerT>
        typename std::enable_if<std::is_same<typename ContainerT::value_type, same_type>::value>::type
            funktion(std::istream& is, ContainerT& cont);
    

  • Mod

    camper schrieb:

    template<typename ContainerT>
        void funktion(std::istream& is, ContainerT& cont)
    {
        typedef typename ContainerT::value_type T;
        static_assert( std::is_same<T, some_type>::value, "unzulässiger Containerelementtyp" );
        ...
    }
    

    Das ist suboptimal; Es überlädt schließlich nicht immer.
    Die zweite Variante wird möglicherweise nicht als Überladung gelten wenn mit VC++ gearbeitet wird.



  • Hab nochmal ne Frage wie trenne ich die Definition von der Deklaration?
    Ich hab das jetzt so gemacht wie in diesen Artikel gezeigt.
    https://www.daniweb.com/software-development/cpp/threads/143139/c-template-success-methods-in-seperate-cpp-file

    Ist das so gut? Gibt es besser Möglichkeiten oder generelle Regeln wie man Templates Organisiert?



  • CraftPlorer schrieb:

    Ist das so gut? Gibt es besser Möglichkeiten oder generelle Regeln wie man Templates Organisiert?

    Wenn man die auslagern möchte, ist das das Prinzip, ja.
    Zwei Kleinigkeiten: Ich würd die Implementierungsdatei nicht .cpp nennen, dann sieht das so aus als inkludiert man ein normales Sourcefile, was man NIE™ machen sollte. Und der Includeguard ist überflüssig, schließlich soll diese Datei nur ein einziges Mal - innerhalb der anderen, die schon via Guard geschützt ist.



  • Ok danke.



  • Hab jetzt nochmal ne Frage zu dem Thema um genau zu sein zu dieser Aussage:

    Nathan schrieb:

    Und der Includeguard ist überflüssig, schließlich soll diese Datei nur ein einziges Mal - innerhalb der anderen, die schon via Guard geschützt ist.

    Wenn ich das ganze so Schreibe:
    Header

    //PARSER_H_
    #include <iostream>
    #include "SerieStruct.h"
    #include <string>
    
    #ifndef FILEPARSER_H_
    #define FILEPARSER_H_
    
    enum class lineIdentifier{ OPEN, CLOSE, INFO, ERROR };
    
    template<template <class, class> class ContainerT,
    	typename Allocator>
    void parseToSerie(std::istream& is, ContainerT< Serie,  Allocator> cont);
    
    struct lineSyntax
    {
    	std::string first;
    	std::string second;
    	lineIdentifier state;
    };
    
    #include "fileParser.tpp"
    #endif
    

    fileParser.tpp

    //FILEPARSER_TPP_
    
    template<template <class, class> class ContainerT,
    	typename Allocator>
    void parseToSerie(std::istream& is, ContainerT<Serie, Allocator> cont)
    {
    	std::cout << "called" << std::endl;
    
    }
    

    Ich bekomme folgende Fehler unter VS2013 Express:

    Fehler	5	error C2923: "ContainerT": "Serie" ist kein gültiges template-Typargument für den <unnamed-symbol>-Parameter.	d:\def stuff\projekte\serienmgr try2\serienmgr 0.0.1\serienmgr 0.0.1\fileparser.tpp	8	1	SerienMgr 0.0.1
    Fehler	6	error C2275: 'ContainerT': Ungültige Verwendung dieses Typs als Ausdruck	d:\def stuff\projekte\serienmgr try2\serienmgr 0.0.1\serienmgr 0.0.1\fileparser.tpp	8	1	SerienMgr 0.0.1
    Fehler	8	error C2182: 'parseToSerie': Unzulässige Verwendung des Typs 'void'	d:\def stuff\projekte\serienmgr try2\serienmgr 0.0.1\serienmgr 0.0.1\fileparser.tpp	8	1	SerienMgr 0.0.1
    Fehler	7	error C2146: Syntaxfehler: Fehlendes ')' vor Bezeichner 'cont'	d:\def stuff\projekte\serienmgr try2\serienmgr 0.0.1\serienmgr 0.0.1\fileparser.tpp	8	1	SerienMgr 0.0.1
    Fehler	4	error C2065: 'Serie': nichtdeklarierter Bezeichner	d:\def stuff\projekte\serienmgr try2\serienmgr 0.0.1\serienmgr 0.0.1\fileparser.tpp	8	1	SerienMgr 0.0.1
    Fehler	2	error C2065: 'istream': nichtdeklarierter Bezeichner	d:\def stuff\projekte\serienmgr try2\serienmgr 0.0.1\serienmgr 0.0.1\fileparser.tpp	8	1	SerienMgr 0.0.1
    Fehler	3	error C2065: 'is': nichtdeklarierter Bezeichner	d:\def stuff\projekte\serienmgr try2\serienmgr 0.0.1\serienmgr 0.0.1\fileparser.tpp	8	1	SerienMgr 0.0.1
    Fehler	9	error C2059: Syntaxfehler: ')'	d:\def stuff\projekte\serienmgr try2\serienmgr 0.0.1\serienmgr 0.0.1\fileparser.tpp	8	1	SerienMgr 0.0.1
    Fehler	1	error C2039: 'istream': Ist kein Element von 'std'	d:\def stuff\projekte\serienmgr try2\serienmgr 0.0.1\serienmgr 0.0.1\fileparser.tpp	8	1	SerienMgr 0.0.1
    

    Wenn ich den Includeguard wieder einfüge compiliert VS ganz normal. Woran liegt das?



  • Sollte nichts mit dem Fehler zu tun haben, aber pack mal die includes ebenfalls in den Includeguard.
    Der Fehler tritt irgendwo anders auf, zeig mal wie SerieStruct.h und main.cpp aussehen.



  • SerieStruct.h

    //SERIESTRUCT_H_
    #include <string>
    #include <vector>
    
    #ifndef SERIESTRUCT_H_
    #define SERIESTRUCT_H_
    
    class Episode
    {
    public:
    	Episode(int nr);
    	Episode(std::string name, int nr);
    	Episode(std::string name, int nr, bool watched);
    
    	std::string getName() const { return _name; }
    	int getNr() const { return _episodeNr; }
    	bool getWatched() const { return _watched; }
    	void setWatched(bool watched) { _watched = watched; }
    	std::string changeName(std::string newName);
    
    private:
    	std::string 	_name;
    	unsigned int 	_episodeNr;
    	bool 			_watched;
    };
    
    class Season
    {
    	using Iter = std::vector<Episode>::iterator;
    public:
    	Season(int nr) : _seasonNr(nr) {}
    
    	int getNr() const  { return _seasonNr; }
    	void changeNr(int newNr) { _seasonNr = newNr;  }
    
    	Iter begin() { return std::begin(_episodes); }
    	Iter end() { return std::end(_episodes); }
    
    	bool addEpisode(std::string name, int nr);
    	bool addEpisode(std::string name, int nr, bool watched);
    	bool operator==(const Season&);
    
    private:
    	std::vector<Episode> _episodes;
    	int	_seasonNr;
    };
    
    class Serie
    {
    	using Iter = std::vector<Season>::iterator;
    public:
    	Serie(std::string name) : _name(name) {}
    
    	std::string getName() const { return _name; }
    	void setName(std::string newName){ _name = newName; }
    
    	Iter begin() { return std::begin(_seasons); }
    	Iter end() { return std::end(_seasons); }
    
    	bool addSeason(int nr);
    
    private:
    	std::vector<Season> _seasons;
    	std::string _name;
    };
    
    #endif
    

    main.cpp

    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include "fileParser.h"
    #include "SerieStruct.h"
    
    int main()
    {
    	std::vector < Serie > serien;
    	parseToSerie(std::cin, serien);
    
    	return 0;
    }
    

    Die Includes von der fileParser.h in Includeguard zu packen hat nichts gebracht. Wenn ich 3 mal F7 drücke also Kompiliere dann erstellt er es beim dritten mal ohne Fehler. Wenn ich dann die fileParser.tpp änder also z.B. Leerzeichen hinzufügen muss ich wider 3 mal Kompilieren damit es funktioniert.



  • CraftPlorer schrieb:

    Die Includes von der fileParser.h in Includeguard zu packen hat nichts gebracht.

    Ja, das war klar, aber das ist gängige Praxis das so zu machen.

    Wenn ich 3 mal F7 drücke also Kompiliere dann erstellt er es beim dritten mal ohne Fehler. Wenn ich dann die fileParser.tpp änder also z.B. Leerzeichen hinzufügen muss ich wider 3 mal Kompilieren damit es funktioniert.

    Sieht nach einem Konfigurationsfehler von MSVC aus, nachdem ich die "-includes aus deinen drei Codeschnipseln manuell gecopy&pasted habe, läuft es nämlich: http://ideone.com/Udnzef



  • Ok. Ich hab mal auf Release Mode gewechselt und jetzt bekomme ich beim ersten mal Kompilieren wieder die gleichen Fehler beim zweiten mal geht es aber mir wird dann nur folgender Fehler angegeben. Weiß jemand damit was anzufangen?

    warning LNK4042: Objekt mehrmals angegeben; zusätzliche Objekte werden ignoriert.
    


  • Ich bin zwar noch ein Frischling in C++, aber kann man da nicht ein delete nehmen und bestimmte Typen zu verbieten?

    Ich hatte mir mal da was notiert.

    void f(double){}       // double erlaubt
    template <typename T>  // alles andere verboten
    void f(T) = delete;
    


  • Objekter schrieb:

    Ich bin zwar noch ein Frischling in C++, aber kann man da nicht ein delete nehmen und bestimmte Typen zu verbieten?

    Ich hatte mir mal da was notiert.
    C++:

    void f(double){}       // double erlaubt
    template <typename T>  // alles andere verboten
    void f(T) = delete;
    

    Ich möchte ja aber das jeder Container funktioniert und nicht nur die die ich freigebe. Aber trotzdem danke.


Anmelden zum Antworten