如何通过应用指定函数来转换 std::variant

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

我需要将

variant<type1, type2, ...>
转换为另一个
variant<ftype1, ftype2, ...>
,其中
ftypeN
是使用
fn()
参数调用的函数
typeN
的返回类型,但需满足以下条件:

  • 如果函数
    fn()
    不可在
    typeN
    上调用,则
    ftypeN
    的类型为
    wrong_arg_type
  • 如果函数
    fn()
    返回
    void
    ,则
    ftypeN
    的类型为
    void_type
  • 否则
    ftypeN
    的类型是
    fn(typeN)
  • 的返回类型

结果

variant
应具有与输入
variant
相同的索引,并且值等于
fn(get<I>(v))
或默认初始化的
void_type
wrong_arg_type
。函数
fn()
最多应调用一次。

这应该适用于三个当前的 C++23 编译器:gcc/clang/MSVC

这是我当前的返回类型推导的实现:

struct void_type{};
struct wrong_arg_type{};

template<class Fn, class...Args>
using ret_t = std::conditional_t<std::is_invocable_v<Fn, Args...>,
    std::conditional_t<std::is_void_v<std::invoke_result_t<Fn, Args...>>, 
        void_type,
        std::invoke_result_t<Fn, Args...>>,
    wrong_arg_type
    >;

template<class Fn, class... Args>
constexpr auto invoke_fn(Fn fn, Args&&...args)
{
    if constexpr(std::is_same_v<ret_t<Fn, Args...>, void_type>)
        return std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...), 
            void_type{};
    else if constexpr(std::is_same_v<ret_t<Fn, Args...>, wrong_arg_type>)
        return wrong_arg_type{};
    else
        return std::invoke(fn, std::forward<Args>(args)...);
}

以及我当前实现的

transform
功能:

template<class Fn, class...T>
auto transform(Fn fn, const std::variant<T...>& v)
{
    using variant_type = std::variant<ret_t<Fn, T>...>;

    return [&]<auto N>(this auto&& self, std::size_t idx, std::index_sequence<N>)
    -> variant_type
    {
        if constexpr(N == sizeof...(T))
            { throw "transform error"; }
        else if (N == idx)
            return variant_type(std::in_place_index<N>, invoke_fn(fn, std::get<N>(v)));
        else
            return self(idx, std::index_sequence<N + 1>{});
    }(v.index(), std::index_sequence<0>{});
}

编译器资源管理器演示位于这里

这段代码有两个问题我目前无法弄清楚。

首先,函数不可调用的情况无法编译(上面演示中的

std::tuple
)。

<source>:15:29: error: no type named 'type' in 'struct std::invoke_result<overloaded<main()::<lambda(double)>, main()::<lambda(int)>, main()::<lambda(const std::string&)> >, std::tuple<int, int> >'
   15 |     std::conditional_t<std::is_void_v<std::invoke_result_t<Fn, Args...>>,
      |                        ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

其次,即使函数是可调用的,MSVC 也会给出一些神秘的错误。

<source>(44): error C2231: '.fn': left operand points to 'class', use '->'

您能建议解决这些问题吗?

c++ variant c++23
1个回答
0
投票

对于第一个错误,请记住

std::conditional
不会短路,所有类型都应该有效。 “延迟”
::type
可能会有所帮助。

template<class Fn, class...Args>
using ret_t = typename std::conditional_t<std::is_invocable_v<Fn, Args...>,
    std::conditional_t<std::is_void_v<std::invoke_result_t<Fn, Args...>>, 
        std::type_identity<void_type>,
        std::invoke_result<Fn, Args...>>,
    std::type_identity<wrong_arg_type>
    >::type;
© www.soinside.com 2019 - 2024. All rights reserved.