遍历 std::variant 的地图
Iterate through a map of std::variant
我正在尝试使用 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
可以用于此吗?从一开始就是个坏主意,我应该使用继承吗?
Can std::variant
be used for this?
是的。您的代码已准备好有效使用变体。变体包含具有相同隐式接口的类型。这是将 std::visit
与通用 lambda 一起使用的绝佳机会。
void MyClass::cycleThroughMap() {
for (auto& [ key, control ] : controlMap) {
std::visit([](auto&& c) {
c.doStuff();
}, control);
}
}
我还冒昧地将成对访问替换为结构化绑定。为了更加简单。
另一种构建代码的方法 - 不再需要 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);
}
}
我正在尝试使用 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
可以用于此吗?从一开始就是个坏主意,我应该使用继承吗?
Can
std::variant
be used for this?
是的。您的代码已准备好有效使用变体。变体包含具有相同隐式接口的类型。这是将 std::visit
与通用 lambda 一起使用的绝佳机会。
void MyClass::cycleThroughMap() {
for (auto& [ key, control ] : controlMap) {
std::visit([](auto&& c) {
c.doStuff();
}, control);
}
}
我还冒昧地将成对访问替换为结构化绑定。为了更加简单。
另一种构建代码的方法 - 不再需要 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);
}
}