2008-10-30 3 views
28

Я хочу иметь карту, которая имеет однородный тип ключа, но разнородные типы данных.как вы делаете гетерогенный boost :: map?

Я хочу быть в состоянии сделать что-то вроде (псевдо-код):

boost::map<std::string, magic_goes_here> m; 
m.add<int>("a", 2); 
m.add<std::string>("b", "black sheep"); 

int i = m.get<int>("a"); 
int j = m.get<int>("b"); // error! 

я мог бы иметь указатель на базовый класс как тип данных, но не хотелось бы.

Я никогда не использовал boost раньше, но посмотрел библиотеку слияния, но не мог понять, что мне нужно делать.

Благодарим за помощь.

+2

`boost :: variant` – 2014-02-10 22:41:55

+0

Если вы знаете все возможные типы, которые вы можете захотеть запечатлеть на карте, тогда` boost :: variant` будет отлично работать. Если вы хотите буквально любого типа, то `boost :: any` - это путь. – Kurt 2014-03-07 05:21:58

ответ

34
#include <map> 
#include <string> 
#include <iostream> 
#include <boost/any.hpp> 

int main() 
{ 
    try 
    { 
     std::map<std::string, boost::any> m; 
     m["a"] = 2; 
     m["b"] = static_cast<char const *>("black sheep"); 

     int i = boost::any_cast<int>(m["a"]); 
     std::cout << "I(" << i << ")\n"; 

     int j = boost::any_cast<int>(m["b"]); // throws exception 
     std::cout << "J(" << j << ")\n"; 
    } 
    catch(...) 
    { 
     std::cout << "Exception\n"; 
    } 

} 
+1

Вместо cumbersome_cast вы можете использовать `std :: decay`. – 2012-03-17 01:17:05

9

How can I build a <favorite container> of objects of different types?

Вы не можете, но вы можете подделать его довольно хорошо. В C/C++ все массивы однородны (т. Е. Все элементы одного типа). Однако с дополнительным слоем косвенности вы можете создать внешний вид гетерогенного контейнера (гетерогенный контейнер - это контейнер, где содержащиеся объекты имеют разные типы).

Существует два случая с гетерогенными контейнерами.

Первый случай возникает, когда все объекты, которые вы хотите сохранить в контейнере, общедоступны из общего базового класса. [...]

Второй случай возникает, когда типы объектов не пересекаются - они не имеют общего базового класса.
Подходом здесь является использование класса дескриптора. Контейнер представляет собой контейнер объектов дескриптора (по значению или по указателю, ваш выбор, по значению проще). Каждый объект дескриптора знает, как «удерживать» (т. Е. Поддерживать указатель) на один из объектов, которые вы хотите поместить в контейнер. Вы можете использовать либо один класс дескриптора с несколькими типами указателей в качестве данных экземпляра, либо иерархию классов дескрипторов, которые затеняют различные типы, которые вы хотите содержать (требуется, чтобы контейнер содержал указатели базового класса дескриптора). Недостатком этого подхода является то, что он открывает класс дескрипторов для обслуживания каждый раз, когда вы меняете набор типов, которые могут содержаться. Преимущество состоит в том, что вы можете использовать класс дескрипторов, чтобы инкапсулировать большую часть уродства управления памятью и временем жизни объекта. Таким образом, использование объектов дескриптора может быть полезным даже в первом случае.

5

Если вы хотите, чтобы ограниченный набор типов поддерживался, Boost.Variant должен сделать трюк.

5

Спасибо, Дэвид, это было то, что мне нужно. Вот рабочее решение.

#include <iostream> 
using std::cout; 
using std::endl; 

#include <map> 
#include <boost/any.hpp> 

using boost::any_cast; 
typedef std::map<std::string, boost::any> t_map; 


int main(int argc, char **argv) 
{ 

    t_map map; 
    char *pc = "boo yeah!"; 

    map["a"] = 2.1; 
    map["b"] = pc; 

    cout << "map contents" << endl; 
    cout << any_cast<double>(map["a"]) << endl; 
    cout << any_cast<char*>(map["b"]) << endl; 

    return 0; 
} 
0

boost any surely works, но я думаю, что использование Int для ввода технологии в качестве ключевого типа карты слияния является лучшим решением. Нет стирания типа и, возможно, быстрее

Смежные вопросы