我有一个枚举类,比如说
enum class color { Red, Green, Blue, /* etc. */};
我有类模板,
template <color Color> class foo;
现在,我想声明一个数据类型,对于
color
的某些(或全部)可能值,它保存 foo<color>
类型的值;我希望能够在运行时插入和删除此类映射。
所以,我想要类似从 color 到 foo 的 unordered_map 之类的东西,除了对于给定的颜色,映射实际上并不是到任何
foo
,而是到 foo<color>
。
我能想到的最接近的东西,也很简洁,是
using MyMap = std::tuple<color::Blue, color::Red, color::Green>
(或者可能是每个 foo 的
std::optional
),因此给定颜色,我可以使用 std::get<foo<MyColor>>(my_map));
并且我还可以编写一个在编译时采用实际颜色的 getter:
template <color Color>
foo<Color> get(const MyMap& map)
{ return std::get<foo<Color>>(map); }
但这一切都有点笨拙。我可以做得更好吗?
备注:
我会从适应开始
std::variant
:
#include <variant>
class color_var{
public:
enum color: std::uint_8{/*list the colors*/};
color_var(auto&& x) //the actual setter
: m_var{std::forward<decltype(x)>(x)}{};
template<color what>
friend auto* get(color_var const &that){
size_t constexpr n{std::to_underlying(what)};
return holds_alternative<n>(m_var)
? std::addressof(get<n>(that.m_var))
: nullptr;
};//! get<what>
friend color to_color(color_var const &that){
return color{static_cast<uint8_t>(index(that.m_var))};
};//! to_color
friend auto& to_variant(color_var const &that)
{ return that.m_var; };
private:
std::variant</*list of types*/> m_var;
};//! class color_var
提供的构造函数扮演放置/赋值的角色。但这个类不是默认可构造的,因为我不知道首选颜色。您可以设置任何值,颜色将自动调整:
color_var cv = type1_in_list{/*init*/};
assert((to_color(cv)==color_var::color1_in_list));
type2_in_list value2{/*init*/};
cv = value2;
assert((to_color(cv)==color_var::color2_in_list));
当颜色已知时,可以检索指向值的非拥有指针以立即使用:
assert((to_color(cv)==my_color));
assert((get<my_color>(cv)));
use(*get<my_color>(cv));
但最好的用法是访问变体:
auto result = std::visit([](auto& value{
if constexpr(std::same_as<type1, remove_cvref_t<decltype(value)>)
return true;
return false;
}, to_variant(cv));
std::visit
是访问变体最快、最安全、最易读的方式。如果您的类型列表都使用一些常见的鸭子类型功能,那么您实际上不需要 lambda 中的 if
语句。
由于语法清晰,我更喜欢友元函数而不是非虚拟公共成员。但您可以让他们成为会员。