簡體   English   中英

C++17 推斷基於 lambda 的訪問者的返回類型

[英]C++17 Infer return type of lambda based visitor

這是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);

其中v是一個variant ,它的所有類型都必須實現 lambdas。

我想向overloaded添加一個函數,該函數具有與所有 lambdas 相同的返回類型(然后是visit(overloaded{...}, v)的返回類型)。 類似於:

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);

這里出了點問題,並且common_type<_Tp...>::type不存在。 我想過使用common_type_t<invoke_result_t<Ts>...>但我沒有 lambdas 的參數,所以invoke_result<Ts..>>::type在那里失敗。

我錯過了什么?

這個問題沒有通用的解決方案,因為如果 lambdas 之一是通用的,例如

[](auto arg) { return arg; }

那么沒有std::variant對象就無法推導出返回類型。

但是,我們可以嘗試從具有非模板operator()那些 lambda 表達式中推斷出常見類型。 這種特殊情況的解決方案如下。

首先創建一個推導 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(...);

然后調整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;
};

最后把所有這些放在一起:

template<class... Ts> struct overloaded : Ts... {
    using Ts::operator()...; 
    static auto test() ->
        std::common_type_t<decltype(return_type(std::declval<Ts>()))...>;
};

例子:

auto fn = overloaded {
    [](auto arg)    { },
    [](int arg)     { return 0;  },
    [](std::string) { return 1.; }
};

static_assert(std::is_same_v<decltype(fn.test()), double>);

如果無法推導出類型,則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