代码之家  ›  专栏  ›  技术社区  ›  Fureeish

我可以得到多个链接函数调用的返回类型吗?

  •  1
  • Fureeish  · 技术社区  · 6 年前

    我希望将函数存储在有序集合中,然后将它们全部应用于某个集合,这将导致获得存储在另一个集合中的经过严重修改的值。我最初的尝试是创建一个 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{};
    }
    

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

    我最终想要实现的是实现Java的流库(这个例子将相当于链接3)。 map 函数)。我还要多拿一些 enum 指示下一个元素是否是 地图 , filter 或者任何其他受支持的函数,所以不会混淆函数应该做什么——现在的问题是从这样的逻辑开始。

    有没有一种方法可以获得链接一个任意数量的函数的返回类型,该类型传递到它所知道的第一个函数?

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

    免责声明-我很清楚即将在 C++20 (希望) rangesV3 . 我试图模仿他们的行为(有些细微的变化)。我也知道 boost::adapters -它们的用法不能满足我的需要,另外我想尝试简单地实现类似的东西。

    1 回复  |  直到 6 年前
        1
  •  3
  •   felix    6 年前

    假设你有三个可调用的对象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;
    }
    

    此代码段也适用于任意数量的链接可调用对象。