У меня есть следующий класс, использующий 3 разных карты: ключи всегда являются строками, а значения могут быть строками, целыми числами или числами с плавающей точкой.
class MyMaps
{
public:
template<typename T> void addKey(const std::string& key);
void addValue(const std::string& key, const std::string& value);
void addValue(const std::string& key, int value);
void addValue(const std::string& key, float value);
private:
std::map<std::string, std::string> stringFields;
std::map<std::string, int> intFields;
std::map<std::string, float> floatFields;
};
addValue()
Функции просто добавляют новую пару в связанную карту. То, над чем я работаю, это addKey()
функция шаблона:
/** Add only a key, the related value is a default one and is specified by template parameter T. */
template<typename T>
void MyMaps::addKey(const string& key)
{
if (typeid(T) == typeid(string))
{
stringFields.insert(pair<string, string>(key, string()));
}
else if (typeid(T) == typeid(int))
{
intFields.insert(pair<string, int>(key, int()));;
}
else if (typeid(T) == typeid(float))
{
floatFields.insert(pair<string, float>(key, float()));
}
}
В основном я использую template
а также typeid()
потому что мне не нравится эта альтернатива, которая опирается на типа-в-имя-функции:
void MyMaps::addStringKey(const string& key)
{
stringFields.insert(pair<string, string>(key, string()));
}
void MyMaps::addIntKey(const string& key)
{
intFields.insert(pair<string, int>(key, int()));
}
void MyMaps::addFloatKey(const string& key)
{
floatFields.insert(pair<string, float>(key, float()));
}
Первый addKey()
версия кажется работает, но мне интересно, есть ли более элегантное решение. Может быть, мне не хватает какой-то объектно-ориентированной концепции дизайна, которая может быть полезна в этом случае?
Заранее спасибо.
Это идеально подходит для специализации шаблона:
template<>
void MyMaps::addKey<string>(const string& key)
{
stringFields.insert(pair<string, string>(key, string()));
}
template<>
void MyMaps::addKey<int>(const int& key)
{
intFields.insert(pair<string, int>(key, int()));;
}
template<>
void MyMaps::addKey<float>(const float& key)
{
floatFields.insert(pair<string, float>(key, float()));
}
РЕДАКТИРОВАТЬ: Для синтаксиса / больше информации о специализации шаблона читайте: Специализация шаблона и частичная специализация шаблона
Или, что еще лучше, если опция Boost является опцией и если ключи уникальны для всех 3 карт, и у вас есть 3 разные карты, чтобы иметь возможность их хранить, тогда подумайте об использовании boost::variant
:
typedef boost::variant<string, int, float> ValueType;
class MyMap
{
public:
typedef std::map<std::string, ValueType> MapType;
template<typename T> void addKey(const std::string& key, T &val)
{
ValueType varVal= val;
allFields.insert(MapType::value_type(key, varVal));
}
private:
MapType allFields;
};
Ваш вопрос запрашивает 2 вещи:
Реальный вопрос, создание ключевого значения Map или Dictionary, используя разные типы, для значений в одной коллекции.
И, потенциальное решение, применяя функцию «typeid».
Дополнительная информация о «typeid»:
http://en.cppreference.com/w/cpp/language/typeid
Ориентация на объект (и класс) великолепна, но иногда вы можете захотеть смешать ее с другими парадигмами.
Как насчет «указателей»?
Указатели позволяют использовать разные типы, как один и тот же простой тип.
Как насчет коллекции словаря значений ключа, в которой хранится строковый ключ и значение указателя, причем указатель может быть целым числом, строкой или объектом.
Или чтобы быть более конкретным. «Значение» может быть кортежем, где первое поле (может быть, перечислимый тип) указывает реальный тип значения. И второе поле «Значение» — это указатель или вариант реального поля.
Первое предложение с использованием «Союзы» (a.k.a. «варианты»), без указателей:
#include <string>
#include <typeinfo>
union ValueUnion
{
int AsInt,
float AsFloat,
std::string& AsStr
};
struct ValueType
{
std::type_info Id,
ValueUnion Value
};
class MyMaps
{
public:
template<typename T> void addKey(const std::string& key);
void addValue(const std::string& key, const std::string& value);
void addValue(const std::string& key, int value);
void addValue(const std::string& key, float value);
private:
std::map<std::string, ValueType> Fields;
};
Или с указателями:
#include <string>
#include <typeinfo>
struct ValueType
{
std::type_info Id,
void* Value
};
class MyMaps
{
public:
template<typename T> void addKey(const std::string& key);
void addValue(const std::string& key, const std::string& value);
void addValue(const std::string& key, int value);
void addValue(const std::string& key, float value);
private:
std::map<std::string, ValueType> Fields;
};
Я видел этот «шаблон» несколько раз, я назвал коллекцию «Key-Value-Type».
Примечание: не много опыта работы с STL, вы уверены, что «std :: map»,
это правильная коллекция?
Просто мои 2 цента.
Может быть не то, что вы хотите, потому что это другой подход, но вы можете использовать карту вариантов. Вы можете определить повышение :: вариант держать только строку, int или float.
Эладидан опередил меня, и я не знаю, как удалить ответы.