【问题标题】:Iterate through a map of std::variant遍历 std::variant 的映射
【发布时间】:2018-05-07 08:35:11
【问题描述】:

我正在尝试使用 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 可以用于此目的吗?从一开始就不是一个坏主意,我应该使用继承吗?

【问题讨论】:

  • 如果你还是调用了同一个函数,为什么要从检查开始呢?您还可以通过将specificTo 函数命名为相同来摆脱doStuff 中的检查。也许对ControlAControlB 使用继承和多态性来给它们一个固定的接口?
  • 是的,直截了当的方法就是这样,我试图了解 c++17 的功能将添加到我的项目中 - 感觉这个例子适合实验(尽管可能因为明显的多态感觉而令人困惑)
  • [OT]:T* control; 未初始化使用。

标签: c++ c++17 variant


【解决方案1】:

std::variant 可以用于这个吗?

是的。您的代码已准备好有效地使用变体。该变体包含具有相同隐式接口的类型。这是将std::visit 与通用 lambda 结合使用的绝佳机会。

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

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

【讨论】:

  • 哈,正是我想要的;这是我的 C++ 初学者不会想到的一些东西。完美运行,非常感谢
【解决方案2】:

另一种构造代码的方式 - 不需要 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);
    }
}

【讨论】:

    猜你喜欢
    • 2023-03-19
    • 2012-05-14
    • 2019-02-26
    • 2018-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-29
    相关资源
    最近更新 更多