将 std::variant 转换为另一个具有类型子集的 std::variant

Convert std::variant to another std::variant with sub-set of types

本文关键字:variant std 类型 子集 转换 另一个      更新时间:2023-10-16

给定一个类型为 std::variant<A,B,C> 的变量,我已经检查过它不包含 C 。如何将其转换为std::variant<A,B>

std::variant<A,B> convert(std::variant<A,B,C> value) {
  if (std::holds_alternative<C>(value)) {
    // die.
  }
  return ... // what to do here?
}

我知道如何使用此处描述的访问者进行相反的操作(转换为超级集变体(,但这种方法不适用于这种情况。

您可以简单地运行槽std::visit,使用仅"吃掉"所需类型的接收 lambda 表达式并创建新的变体类型。

提示:由于转换后的变体类型可能是"空的",因为您必须返回某些内容,即使传入变体中的类型在您的定义中不是有效的类型,您也必须提供一些"虚拟类型"。为此,已经定义了std::monotype

使用 c++20 概念会更容易,但我也为您提供了手工制作的 lambda 的"老式"方式:

struct A{};
struct B{};
struct C{};
using VARIANT_ALL = std::variant<A,B,C>;
using VARIANT_SUB = std::variant<std::monostate,A,B>;
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template < typename CHECK_ME >
concept IS_WANTED = std::is_same_v<CHECK_ME, A> || std::is_same_v<CHECK_ME, B >;  
VARIANT_SUB GetSubset( const VARIANT_ALL& vin )
{
    return std::visit( overloaded
               {
                   []( const A& x ){ return VARIANT_SUB{x};},
                   []( const B& x ){ return VARIANT_SUB{x};},
                   []( auto&  ) { return VARIANT_SUB{};}
               }, vin 
              );
}
VARIANT_SUB GetSubset2( const VARIANT_ALL& vin )
{
    return std::visit( overloaded
               {
                   [] < IS_WANTED TYPE >( const TYPE& x ){ return VARIANT_SUB{x};},
                   []( auto&  ) { return VARIANT_SUB{};}
               }, vin 
              );
}
int main()
{
    VARIANT_ALL va=A{};
    VARIANT_ALL vb=B{};
    VARIANT_ALL vc=C{};
    auto vva = GetSubset( va );
    auto vvb = GetSubset( vb );
    auto vvc = GetSubset( vc );
    std::cout << std::holds_alternative<A>( vva ) << std::endl;
    std::cout << std::holds_alternative<B>( vvb ) << std::endl;
    std::cout << std::holds_alternative<std::monostate>( vvc ) << std::endl;
    auto vva2 = GetSubset2( va );
    auto vvb2 = GetSubset2( vb );
    auto vvc2 = GetSubset2( vc );
    std::cout << std::holds_alternative<A>( vva2 ) << std::endl;
    std::cout << std::holds_alternative<B>( vvb2 ) << std::endl;
    std::cout << std::holds_alternative<std::monostate>( vvc2 ) << std::endl;
}