将`std :: any`转换为`std :: variant`

问题描述 投票:0回答:1

std::any除了存储的类型是已知的还是可疑的,没有什么要做的(除了存储它)。然后可以查询(type())或强制转换(any_cast)。但是,什么时候需要处理多种类型而不是一种类型呢?在这种情况下,一种解决方案是将其转换为std::variant

例如API提供std::any对象,但只需要有限的一组类型,并且这些对象需要存储在容器中(矢量,树等)。

如何将std::any转换为std::variant


免责声明:std::any主要用于库代码,其目的是在某些巧妙的模板和类型擦除中替换void *。与任何新事物一样,std::any可能会被过度使用和滥用。如果std::any是您代码的正确解决方案,请三思。

c++ c++17 variant
1个回答
3
投票

此代码将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_checkfalse,则:
      (any_cast, true)的短路,未评估[&&
  • [(type_check && (any_cast, true))等于false
  • 对折叠表达式中的下一个运算进行求值
  • 如果type_checktrue,则:
    • (any_cast, true)被评估:
      • any_cast被评估。变体从any对象获取值。
    • [any_cast结果被丢弃
    • [true被评估
    • [(any_cast, true)等于true
  • [(type_check && (any_cast, true))等于true
  • 由于||的短路,未评估其余折数>>
  • 整个表达式(倍数)为true
  • 如果没有type_check计算为true,则整个表达式(倍数)计算为false
  • © www.soinside.com 2019 - 2024. All rights reserved.