Wechseln Sie den Vorlagentyp

  • Ich möchte Speicher für mein Spiel erstellen. Nun sieht der Code folgendermaßen aus:

     class WorldSettings
    {
        private:
            std::map<std::string, int> mIntegerStorage;
            std::map<std::string, float> mFloatStorage;
            std::map<std::string, std::string> mStringStorage;
    
        public:
            template <typename T>
            T Get(const std::string &key) const
            {
                // [?]
            }
    };
     

    Ich habe also einige assoziative Container, die den genauen Datentyp speichern . Nun möchte ich in den Einstellungen einen Wert hinzufügen: settings.Push<int>("WorldSize", 1000); und ihn abrufen: settings.Get<int>("WorldSize");. Aber wie man die benötigte Karte aufgrund des übergebenen Typs in die Vorlage umschaltet?

    Oder vielleicht wissen Sie einen besseren Weg, danke.

    22 November 2011
    Ockonal
3 answers
  • Da Sie vor C ++ 11 keine Funktionen spezialisieren können, müssen sich Ihre Mitgliedsfunktionen in der Signatur unterscheiden. Der Rückgabetyp zählt nicht. Aus meiner Erfahrung mit einigen Compilern können Sie darauf verzichten, aber wie üblich sollten Sie Ihren Code so nah wie möglich an den Standard halten.

    Das heißt, Sie können hinzufügen ein Dummy-Parameter, der die Leistung und die Art und Weise, wie Sie die Funktion aufrufen, nicht beeinflusst:

     public:
        template <typename T>
        T Get(const std::string &key) const
        {
            return GetInner(key, (T*)0);
        }
    
    private:
        int GetInner(const std::string& key, int*) const
        {
            // return something from mIntegerStorage
        }
    
        float GetInner(const std::string& key, float*) const
        {
            // return something from mFloatStorage
        }
     

    Und so weiter. Sie bekommen die Idee.

    22 November 2011
    gwiazdorrr
  • Seths Antwort ist ideal. Wenn Sie jedoch keinen Zugriff auf einen C ++ 11-Compiler haben, können Sie stattdessen die Spezialisierung für die Template-Klasse verwenden. Es ist viel ausführlicher, behält jedoch die gleiche Funktionalität bei.

     class WorldSettings
    {
        template<class T>
        struct Selector;
        template<class T>
        friend struct Selector;
    
    private:
        std::map<std::string, int> mIntegerStorage;
        std::map<std::string, float> mFloatStorage;
        std::map<std::string, std::string> mStringStorage;
    
    public:
        template <typename T>
        T Get(const std::string &key)
        {
            return Selector<T>::Get(*this)[key];
        }
    };
    
    template<>
    struct WorldSettings::Selector<int>
    {
        static std::map<std::string, int> & Get(WorldSettings &settings)
        {
            return settings.mIntegerStorage;
        }
    };
    
    template<>
    struct WorldSettings::Selector<float>
    {
        static std::map<std::string, float> & Get(WorldSettings &settings)
        {
            return settings.mFloatStorage;
        }
    };
    
    // etc.
     
    22 November 2011
    Etienne de Martel
  • In C ++ 03 würde ich die Verwendung von "boost :: any" im Typ des Containers und die. Sie benötigen einen einzelnen Accessor:

    std::map<std::string,boost::any> storage;
    template <typename T> getValue( std::string const & key ) {
       return boost::any_cast<T>( storage[key] );
    }
     

    Dies ist eine grobe Skizze, als Member-Funktion. Es wäre const, und es sollte 'map :: find' verwenden, um den Container bei der Suche nicht zu ändern, es sollte sich mit ungültigen Joeys befassen und wahrscheinlich die Boost-Ausnahmen in Ihren eigenen Anwendungsausnahmen neu zuordnen.

    22 November 2011
    David Rodríguez - dribeas