此代码采用std::any 对象和类型列表并将对象转换为std::variant,如果存储的类型不是给定类型之一,则抛出std::bad_any_cast。
#include <any>
#include <variant>
#include <optional>
#include <typeinfo>
template <class... Args>
auto any_to_variant_cast(std::any a) -> std::variant<Args...>
{
if (!a.has_value())
throw std::bad_any_cast();
std::optional<std::variant<Args...>> v = std::nullopt;
bool found = ((a.type() == typeid(Args) && (v = std::any_cast<Args>(std::move(a)), true)) || ...);
if (!found)
throw std::bad_any_cast{};
return std::move(*v);
}
示例用法:
auto test(const std::any& a)
{
auto v = any_to_variant_cast<int, std::string>(a);
std::visit([](auto val) { std::cout << val << std::endl; }, v);
}
Code on godbolt
一些解释:
之所以使用std::optional<std::variant<Args...>>,是因为std::variant<Args...> default constructor 构造了保存第一个备选方案的值初始化值的变量,并且要求第一个备选方案是默认可构造的。
((a.type() == typeid(Args) && (v = std::any_cast<Args>(std::move(a)), true)) || ...)
// ------------------------ -------------------------------------
// type_check any_cast
这是fold expression。我已经重命名了一些子表达式以便于解释。随着重命名,表达式变为:
// ((type_check && (any_cast, true)) || ...)
- 如果
type_check 是false 那么:
-
由于
&& 短路,(any_cast, true) 未评估
-
(type_check && (any_cast, true)) 评估为 false
- 计算折叠表达式中的下一个操作
- 如果
type_check 是true 那么:
-
(any_cast, true) 被评估:
-
any_cast 被评估。变体从任何对象中获取值。
-
any_cast 结果被丢弃
-
true 被评估
-
(any_cast, true) 评估为 true
-
(type_check && (any_cast, true)) 评估为 true
- 由于
|| 的短路,未评估其余折叠
- 整个表达式(折叠)计算为
true
- 如果没有
type_check 计算为true,则整个表达式(折叠)计算为false