Переключить тип шаблона - PullRequest
3 голосов
/ 22 ноября 2011

Я хочу создать хранилище для моей игры. Теперь код выглядит так:

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
        {
            // [?]
        }
};

Итак, у меня есть несколько ассоциативных контейнеров, в которых хранится точный тип данных. Теперь я хочу добавить в настройки какое-то значение: settings.Push<int>("WorldSize", 1000); и получить его: settings.Get<int>("WorldSize");. Но как переключить нужную карту из-за переданного типа в шаблон?

Или, может быть, вы знаете лучший способ, спасибо.

Ответы [ 4 ]

8 голосов
/ 22 ноября 2011

Прежде всего, поскольку до C ++ 11 вы не могли специализировать функции, ваши функции-члены должны отличаться сигнатурой - тип возвращаемого значения не учитывается. Исходя из моего опыта работы с некоторыми компиляторами, вы можете обойтись без него, но, как обычно, ваш код должен быть как можно ближе к стандартному. При этом вы можете добавить фиктивный параметр, который не повлияет на производительность и способ вызова функции:

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
    }

И так далее. Вы поняли идею.

8 голосов
/ 22 ноября 2011

Если ваш компилятор поддерживает это 1 , вы можете использовать специализации шаблонных функций:

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); // purposely left undefined
};

...

template<>
int WorldSettings::Get<int>(const std::string& key) {
    return mIntegerStorage[key];
}

template<>
float WorldSettings::Get<float>(const std::string& key) {
    return mFloatStorage[key];
}

// etc

Обратите внимание, что методы не const, поскольку map<>::operator[] не const.

Кроме того, если кто-то попытается использовать шаблон с типом, отличным от того, для которого вы указали специализацию, он получит ошибки компоновщика, поэтому ваш код не будет работать неправильно или что-либо еще.Что оптимально.

1 Если нет, см. @ gwiazdorrr ответ

2 голосов
/ 22 ноября 2011

Ответ Сета идеален, но если у вас нет доступа к компилятору C ++ 11, вы можете использовать специализацию класса шаблона и сделать это вместо этого. Это намного более многословно, но сохраняет ту же функциональность.

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.
1 голос
/ 22 ноября 2011

В C ++ 03 я бы порекомендовал использовать any boost :: any container в типе контейнера и. Вам нужен один аксессор:

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

Это грубый набросок, поскольку функция-член будет константной, и она должна использовать 'map :: find', чтобы не изменять контейнер при поиске, он должен иметь дело с недопустимыми joeys и, вероятно, переназначить исключения буста в исключения из вашего собственного приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...