Сохранить несколько типов как значения в словаре С++?

Я хочу написать объект C++, который ведет себя почти так же, как словарь Python. std::map и std::unordered_map в C++ поддерживают некоторые функции, которые уже есть в словарях Python, но лишены одной из наиболее важных возможностей, а именно возможности добавления произвольных объектов и типов. Даже если это невозможно, насколько близко вы сможете использовать возможности словаря Python?

Несколько предыдущих вопросов (здесь и здесь) не решают проблему добавления нескольких типов в словарь.

Например, я хочу иметь возможность сделать что-то подобное на C++:

my_dict = {'first': 1,
           'second': "string",
           'third': 3.5,
           'fourth': my_object}
my_dict['fifth'] = list([1, 2, 3])

Лучшим решением, которое я могу придумать, было бы использование указателей void на данные, которые переинтерпретируются, или, возможно, какой-то полиморфизм времени выполнения с некоторыми ограничениями типа?

Возможный дубликат C++ эквивалент словарей Python

Lord Elrond 29.05.2019 00:36

@CalebGoodman Я так не думаю. Оператор спрашивает о вставке элементов произвольного типа.

George 29.05.2019 00:37

Это вроде как возможно, но код будет выглядеть некрасиво. Вам также нужно подумать о том, как вы печатаете извлеченный элемент. Похоже на проблему XY или, по крайней мере, просто для развлечения, но я могу ошибаться, у вас есть вариант использования?

George 29.05.2019 00:42

Откровенно говоря, использование разнородных типов в контейнерах Python, хотя и определенно возможно, не является чем-то, что я бы назвал одной из «наиболее важных возможностей». На самом деле, я бы сказал, что наличие разнородных типов в ваших контейнерах почти всегда является плохим дизайном.

juanpa.arrivillaga 29.05.2019 00:46

Связано: С++ std::map, содержащий ЛЮБОЙ тип значения (охватывает значения).

ShadowRanger 29.05.2019 00:47

Также связано: Другой тип ключа на карте. Это указывает на то, что это выполнимо с повышением, выполнение без повышения было (по крайней мере, когда был задан вопрос) более ограниченным. Не знаю, помогут ли здесь дополнения С++ 17 (например, std::variant/std::any).

ShadowRanger 29.05.2019 00:50
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
7
6
2 649
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

The best solutions that I can think would be like using void pointers to data which get reinterpreted, or perhaps some kind of run-time polymorphism with some type restrictions?

На мой взгляд, большое количество указателей void и множество переинтерпретаций указателей в современном C++ должны быть сигналом о плохом дизайне. Я считаю, что полиморфизм был бы выходом.

Кроме того, если у вас есть фиксированное количество типов, которые вы хотите использовать, рассмотрите возможность использования C++ 17 станд:: любой или станд::вариант, которые являются более современными union.

#include <iostream>
#include <map>
#include <string>
#include <variant>

typedef std::map<std::variant<int, std::string>, std::variant<int, std::string>> Dict;

int main(){
    Dict d;
    d["key"] = 1;
    d[5] = "woah";
    std::cout << std::get<int>(d["key"]) << std::endl; // prints 1

    // edit: print all the keys in d
    for(auto& k_v: d){
        std::visit(
            [](auto& value){std::cout << value << ' ';},
            k_v.first // k_v.second to print all values in d
        );
    }

}

Приведенный выше пример может быть полезен в некоторых случаях использования.

Также проверьте, как объекты json реализованы в любой библиотеке C++ json. Это может быть полезно.

Редактировать: я добавил пример std::visit, который перебирает ключи в нашем словаре.

Кроме того, не делай этого. Предпочитаю всегда знать интерфейс значений, с которыми нужно работать.

Mooing Duck 29.05.2019 00:57

Что вы имеете в виду под не делай этого? Во-первых, использование таких многотипных словарей не очень хорошая идея, поэтому, если не делай этого соответствует этому, я согласен.

asikorski 29.05.2019 01:01

Не могли бы вы добавить короткий пример, показывающий, как использовать std::visit? Это предпочтительный интерфейс для std::variant, и часто его проще использовать, чем вызывать std::get

Alecto Irene Perez 29.05.2019 01:03

Я искал похожее решение для жесткого кодирования параметров моих python экспериментов в c++. Я нашел модуль std'17 variant очень полезным.

#include <iostream>
#include <map>
#include <variant>
#include <vector>
#include <string>

int main(){
    typedef std::map<std::variant<std::string, int>, std::variant<int, double, float, std::string>> Dict;

    std::vector<Dict> params = {
        {
            {"label", "Labs"},
            {"dir", "/media/sf_Data/"},
            {"num_frames", 4},
            {"scale_factor", 1.0},
            {5, "my number five"},
        }, // Dict 0
        {
            {"label", "Airplanes"},
            {"dir", "/media/m_result/"},
            {"num_frames", 5},
            {"scale_factor", 0.5},
            {5, "number five"},
        } // Dict 1
    };

    int idx = 1;
    std::string label   = std::get<std::string>(params[idx]["label"]);
    std::string folder  = std::get<std::string>(params[idx]["dir"]);
    int num_frames      = std::get<int>(params[idx]["num_frames"]);
    double scf          = std::get<double>(params[idx]["scale_factor"]);
    std::string nbstr   = std::get<std::string>(params[idx][5]);
    
    std::cout << label << std::endl;
    std::cout << folder << std::endl;
    std::cout << num_frames << std::endl;
    std::cout << scf << std::endl;
    std::cout << nbstr << std::endl;

    return 0;
}

Результат:

Airplanes
/media/m_result/
5
0.5
number five

Другие вопросы по теме