Итерация по карте std :: option - PullRequest
0 голосов
/ 07 мая 2018

Я экспериментирую с C ++ 17 std :: variable для хранения данных нескольких типов на карте. В данном случае используется карта контроллеров универсальных типов (но ограниченная std::variant), которую я могу перебирать и вызывать методы. В приведенном ниже примере,

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

class ControlA {
public:
    void specificToA() { std::cout << "A" << std::endl; }
};

class ControlB {
public:
    void specificToB() { std::cout << "B" << std::endl; }
};

template<typename T>
class ControlItem{
    T* control;

public:
    ControlItem() = default;
    ~ControlItem() = default;

    void doStuff() {
        if constexpr (std::is_same_v<T, ControlA>) {
            control->specificToA();
        }
        if constexpr (std::is_same_v<T, ControlB>) {
            control->specificToB();
        }
    }
};

class MyClass {
public:
    void cycleThroughMap();
    std::map<std::string, std::variant<ControlItem<ControlA>, ControlItem<ControlB>>> controlMap;
};

Эвристический метод для этого будет получить сопоставленное значение каждого объявленного типа, например:

void MyClass::cycleThroughMap() {
    for (auto controlItem : controlMap) {
        if (auto control = std::get_if<ControlItem<ControlA>>(&controlItem.second)) {
            control->doStuff();
         } else if (auto control = std::get_if<ControlItem<ControlB>>(&controlItem.second)) {
            control->doStuff();
         } else
            std::cout << "Unknown type!" << std::endl;
    }
}

Это работает, но кажется, что его не существует.
Можно ли использовать std::variant для этого? Это плохая идея с самого начала, я должен использовать наследование и вуаля?

Ответы [ 2 ]

0 голосов
/ 07 мая 2018

Еще один способ структурирования кода - устраняет необходимость в get_if. Встроенные комментарии:

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

class ControlA {
public:
    void specificToA() { std::cout << "A" << std::endl; }
};

// consistent free-function interface for each operation type allows ADL lookup
void adlDoStuff(ControlA& c)
{
    // but with different implementation details
    c.specificToA();
}

class ControlB {
public:
    void specificToB() { std::cout << "B" << std::endl; }
};

// consistent free-function interface for each operation type allows ADL lookup
void adlDoStuff(ControlB& c)
{
    // but with different implementation details
    c.specificToB();
}

template<typename T>
class ControlItem{
    T* control;

public:
    ControlItem() = default;
    ~ControlItem() = default;

    void doStuff() {
        // invoke the adl-friendly free functions.
        adlDoStuff(*control);
    }
};

class MyClass {
public:
    void cycleThroughMap();
    std::map<std::string, std::variant<ControlItem<ControlA>, ControlItem<ControlB>>> controlMap;
};

void MyClass::cycleThroughMap() {
    // use std::visit. Every type of control will have the .doStuff interface
    for (auto&& elem : controlMap) {
        std::visit([](auto&& control)
        {
            control.doStuff();
        }, elem.second);
    }
}
0 голосов
/ 07 мая 2018

Может ли std::variant использоваться для этого?

Да. Ваш код рассчитан на эффективное использование варианта. Вариант содержит типы с тем же неявным интерфейсом. Это прекрасная возможность использовать std::visit с общей лямбдой.

void MyClass::cycleThroughMap() {
    for (auto& [ key, control ] : controlMap) {
        std::visit([](auto&& c) {
          c.doStuff();
        }, control);
    }
}

Я также позволил себе заменить парный доступ структурированным связыванием. Для некоторой дополнительной простоты.

...