迭代 std::variant 的映射

问题描述 投票:0回答:2

我正在尝试使用 C++17 的 std::variant 在映射中存储多种类型的数据。这里的用例是拥有一个泛型类型的控制器映射(但受

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
可以用来做这个吗?从一开始这就是一个坏主意吗?我应该使用继承吗?

c++ c++17 stdmap variant std-variant
2个回答
13
投票

可以用

std::variant
来做这个吗?

是的。您的代码已准备好有效地使用变体。该变体持有具有相同隐式接口的类型。这是将

std::visit
与通用 lambda 一起使用的绝佳机会。

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

我还冒昧地将配对访问替换为结构化绑定。为了增加一些简单性。


0
投票

另一种构建代码的方法 - 消除了对 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);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.