我需要将
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 '->'
您能建议解决这些问题吗?
对于第一个错误,请记住
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;