【问题标题】:Can I get the return type of multiple chained functions calls?我可以获得多个链式函数调用的返回类型吗?
【发布时间】:2019-01-07 14:59:46
【问题描述】:

我想将函数存储在有序集合中,然后将它们全部应用于某个集合,这将导致获得大量修改的值,存储在另一个集合中。我最初的尝试包括创建一个上述函数的std::tuple,并尝试获取将所有这些函数应用于某个类型的结果类型(std::invoke_result):

int main() {
    auto multiply   = [](const auto arg){ return arg * arg; };
    auto change     = [](const auto arg){ return std::vector{arg}; };
    auto to_string  = [](const auto arg){ return arg.size() + " size"; };

    auto functions = std::make_tuple(multiply, change, to_string);

    std::vector<int> source{1, 2, 3, 4};

    using f_type = decltype(functions);
    using last_type =
            std::tuple_element_t<std::tuple_size_v<f_type> - 1, f_type>;
    using result_type =
            std::invoke_result_t<last_type, /* size - 2 ret type and so on */>;

    /* 
     * result_type is the type of applying *multiply* to int (type of *source*),
     * then applying *change* to the result of *multiply* and then applying
     * *to_string* to the result of *change*. Should be std::string.
     */
    std::vector<result_type> results{};
}

问题是std::invoke_result_t 的第二个template 参数需要一个类型,该类型将传递给last_type 类型的对象的调用运算符。这需要在最后一个元素的返回类型之前扣除一个,等等(可能有很多函数)。

我最终想要实现的是实现 Java 的流库(这个例子相当于链接 3 个map 函数)。我还将持有额外的enums,它将指示下一个元素是mapfilter 还是任何其他受支持的函数,因此不会混淆该函数应该做什么——现在的问题是从这样的逻辑开始。

有没有办法获得链接任意数量的函数的返回类型,其中类型传递给它知道的第一个?

或者我的设计有太多缺陷,我宁愿重新开始,遵循完全不同的逻辑?

免责声明 - 我很清楚C++20(希望)rangesV3 即将推出。我试图模仿他们的行为(有一些小的变化)。我也知道boost::adapters - 他们的用法让我不满意,而且我想尝试简单地实现类似的东西。

【问题讨论】:

  • 你考虑过类似using ResultType = decltype(to_string(change(multiply(source[0]))));的东西吗?
  • @lubgr 否,因为存储在std::tuple 中的这些函数的数量可能非常高,而且并不总是已知的。我正在寻找一个通用的解决方案 - 给你一个 std::tuple 并且你需要使用它(可能以 template 函数中参数包的 std::tuple 的形式)。我很想在某种折叠表达式中迭代元组,并通过 source[0] 指定第一个函数的参数,但我无法实现这样的想法。

标签: c++ function stdtuple


【解决方案1】:

假设你有三个可调用对象 f g h,并且你想得到h(g(f(args...)))的类型,你可以这样做:

template <size_t first, class ChainedFns, class... Args>
decltype(auto) Call(ChainedFns &&fns, Args&&... args) {
    if constexpr (std::tuple_size_v<std::decay_t<ChainedFns>> == 0)
        return;
    else if constexpr (first < std::tuple_size_v<std::decay_t<ChainedFns>>-1)
        return Call<first + 1>(fns, std::invoke(std::get<first>(std::forward<ChainedFns>(fns)), std::forward<Args>(args)...));
    else if constexpr (first == std::tuple_size_v<std::decay_t<ChainedFns>>-1)
        return std::invoke(std::get<first>(std::forward<ChainedFns>(fns)), std::forward<Args>(args)...);
}

template <size_t first, class ChainedFns, class... Args>
struct invoke_result_of_chained_callables {
    using type = decltype(Call<first>(std::declval<ChainedFns>(), std::declval<Args>()...));
};

template <size_t first, class ChainedFns, class... Args>
using invoke_result_of_chained_callables_t = typename invoke_result_of_chained_callables<first, ChainedFns, Args...>::type;

int main() {
    auto fns = std::make_tuple(
        [](auto) { return 0; }, // f
        [](auto x) { return std::vector{ x }; }, // g
        [](auto x) { return x.size(); } // h
    );

    using type = decltype(Call<0>(fns, nullptr));
    static_assert(std::is_same_v<type, size_t>);

    using type1 = invoke_result_of_chained_callables_t<0, decltype(fns), std::nullptr_t>;
    static_assert(std::is_same_v<type, type1>);
    return 0;
}

此代码 sn-p 也适用于任意数量的链式可调用对象。

【讨论】:

  • 工作就像一个魅力,特别是考虑到我实际上可以获得链接函数的价值。谢谢你,巨大的 +1!
  • @Fureeish 欢迎您。我对其进行了一些改进,使其可以处理所有值类别。我还添加了一个类似特征的助手,当你只有类型时,这可以为你省去一些麻烦。在这种情况下,简历和价值类别问题可能会令人困惑,请随时在此社区中提问。 :)
猜你喜欢
  • 2012-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-16
  • 1970-01-01
  • 2021-12-01
  • 2019-02-10
相关资源
最近更新 更多