как сделать гетерогенный буст :: карта? - PullRequest
31 голосов
/ 30 октября 2008

Я хочу иметь карту с однородным типом ключа, но с разнородными типами данных.

Я хочу иметь возможность сделать что-то вроде (псевдокод):

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 раньше, но смотрел библиотеку fusion, но не могу понять, что мне нужно делать.

Спасибо за вашу помощь.

Ответы [ 6 ]

38 голосов
/ 31 октября 2008
#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";
    }

}
10 голосов
/ 30 октября 2008

Как создать <любимый контейнер> объектов разных типов?

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

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

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

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

8 голосов
/ 30 октября 2008

Может ли повысить :: любой для вас?

6 голосов
/ 31 октября 2008

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

#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;
}
5 голосов
/ 30 октября 2008

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

0 голосов
/ 14 мая 2017

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

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