[英]C++17 Infer return type of lambda based visitor
Here's the lambda overload based visitor given in https://en.cppreference.com/w/cpp/utility/variant/visit :这是https://en.cppreference.com/w/cpp/utility/variant/visit 中给出的基于 lambda 重载的访问者:
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
std::visit(overloaded {
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; },
}, v);
where v
is a variant
and all its types must have implemented lambdas.其中v
是一个variant
,它的所有类型都必须实现 lambdas。
I want to add a function to overloaded
that has the same return type as all lambdas (which is then the return type of visit(overloaded{...}, v)
).我想向overloaded
添加一个函数,该函数具有与所有 lambdas 相同的返回类型(然后是visit(overloaded{...}, v)
的返回类型)。 Something similar to:类似于:
template<class... Ts> struct overloaded : Ts...
{
using Ts::operator()...;
common_type_t<decltype(declval<Ts>())...> test() const { return {}; }
};
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
auto r = std::visit(overloaded {
[](auto arg) { return 1; },
[](double arg) { return 2; },
[](const std::string& arg) { return 3; },
}, v);
Here something goes wrong and common_type<_Tp...>::type
doesn't exist.这里出了点问题,并且common_type<_Tp...>::type
不存在。 I thought about using common_type_t<invoke_result_t<Ts>...>
but I don't have the lambdas' arguments, so invoke_result<Ts..>>::type
fails there.我想过使用common_type_t<invoke_result_t<Ts>...>
但我没有 lambdas 的参数,所以invoke_result<Ts..>>::type
在那里失败。
What am I missing?我错过了什么?
This problem has no general solution, because if one of the lambdas is generic, eg这个问题没有通用的解决方案,因为如果 lambdas 之一是通用的,例如
[](auto arg) { return arg; }
then there is no way to deduce the return type without std::variant
object.那么没有std::variant
对象就无法推导出返回类型。
However, we can try to deduce common type from those lambdas that have a non-template operator()
.但是,我们可以尝试从具有非模板operator()
那些 lambda 表达式中推断出常见类型。 The solution for this special case follows.这种特殊情况的解决方案如下。
First make a trait that deduces the return type of a lambda, or returns empty
if deduction fails:首先创建一个推导 lambda 返回类型的特征,如果推导失败则返回empty
:
struct empty {};
template<typename Ret, typename Fn, typename Arg>
Ret return_type(Ret(Fn::*)(Arg));
template<typename Ret, typename Fn, typename Arg>
Ret return_type(Ret(Fn::*)(Arg) const);
template<typename Fn>
auto return_type(Fn) -> decltype(return_type(&Fn::operator()));
empty return_type(...);
Then tweak std::common_type
:然后调整std::common_type
:
template<>
struct std::common_type<empty, empty> {
using type = empty;
};
template<typename T>
struct std::common_type<T, empty> {
using type = T;
};
template<typename T>
struct std::common_type<empty, T> {
using type = T;
};
And finally put all this together:最后把所有这些放在一起:
template<class... Ts> struct overloaded : Ts... {
using Ts::operator()...;
static auto test() ->
std::common_type_t<decltype(return_type(std::declval<Ts>()))...>;
};
Example:例子:
auto fn = overloaded {
[](auto arg) { },
[](int arg) { return 0; },
[](std::string) { return 1.; }
};
static_assert(std::is_same_v<decltype(fn.test()), double>);
If no type can be deduced, the return type of test()
will be empty
:如果无法推导出类型,则test()
的返回类型将为empty
:
auto fn = overloaded {
[](auto arg) { }
};
static_assert(std::is_same_v<decltype(fn.test()), empty>);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.