如何解决我如何编码类似std :: variant的开关?
当var = std::variant<std::monostate,a,b,c>
是某些类型时,我有一些a,c
。
如何在运行时检查var
包含的类型?
在官方文档中,我发现以下信息:如果var
包含a
类型,并且我写了std::get<b>(var)
,则会出现异常。所以我想到了这个解决方案:
try {
std::variant<a>(var);
// Do something
} catch(const std::bad_variant_access&) {
try {
std::variant<b>(var);
// Do something else
} catch(const std::bad_variant_access&) {
try {
std::variant<c>(var);
// Another else
} catch (const std::bad_variant_access&) {
// std::monostate
}
}
}
但是它是如此的复杂和丑陋!有没有更简单的方法来检查std::variant
包含的类型?
解决方法
最简单的方法是基于当前的std::variant::index()
switch
。这种方法要求您的类型(std::monostate
,A
,B
,C
)必须始终保持相同的顺序。
// I omitted C to keep the example simpler,the principle is the same
using my_variant = std::variant<std::monostate,A,B>;
void foo(my_variant &v) {
switch (v.index()) {
case 0: break; // do nothing because the type is std::monostate
case 1: {
doSomethingWith(std::get<A>(v));
break;
}
case 2: {
doSomethingElseWith(std::get<B>(v));
break;
}
}
}
如果您的可调用电话适用于任何类型,则也可以使用std::visit
:
void bar(my_variant &v) {
std::visit([](auto &&arg) -> void {
// Here,arg is std::monostate,A or B
// This lambda needs to compile with all three options.
// The lambda returns void because we don't modify the variant,so
// we could also use const& arg.
},v);
}
如果您不希望std::visit
接受std::monostate
,则只需检查index
是否为0。再一次,这取决于std::monostate
是第一种类型变种,因此始终使其成为第一个是很好的做法。
您还可以使用可调用对象内的if-constexpr
检测类型。使用这种方法,参数不再必须具有相同的顺序:
void bar(my_variant &v) {
std::visit([](auto &&arg) -> my_variant {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<std::monostate,T>) {
return arg; // arg is std::monostate here
}
else if constexpr (std::is_same_v<A,T>) {
return arg + arg; // arg is A here
}
else if constexpr (std::is_same_v<B,T>) {
return arg * arg; // arg is B here
}
},v);
}
请注意,第一个lambda返回void
,因为它只处理变量的当前值。如果要修改变体,则lambda需要再次返回my_variant
。
您可以在std::visit
内部使用重载的访客来分别处理A
或B
。有关更多示例,请参见std::visit
。
std::visit
是必经之路:
甚至有overloaded
允许内联访问者:
// helper type for the visitor #4
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of C++20)
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
`overloaded`
等等:
std::visit(overloaded{
[](std::monostate&){/*..*/},[](a&){/*..*/},[](b&){/*..*/},[](c&){/*..*/}
},var);
要改为使用链接的if分支,可以使用std::get_if
if (auto* v = std::get_if<a>(var)) {
// ...
} else if (auto* v = std::get_if<b>(var)) {
// ...
} else if (auto* v = std::get_if<c>(var)) {
// ...
} else { // std::monostate
// ...
}
,
您可以使用标准的std::visit
用法示例:
#include <variant>
#include <iostream>
#include <type_traits>
struct a {};
struct b {};
struct c {};
int main()
{
std::variant<a,b,c> var = a{};
std::visit([](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T,a>)
std::cout << "is an a" << '\n';
else if constexpr (std::is_same_v<T,b>)
std::cout << "is a b" << '\n';
else if constexpr (std::is_same_v<T,c>)
std::cout << "is a c" << '\n';
else
std::cout << "is not in variant type list" << '\n';
},var);
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。