简体   繁体   English

C++17 推断基于 lambda 的访问者的返回类型

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM