首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >传递多个函数并将其结果存储在一个元组中。

传递多个函数并将其结果存储在一个元组中。
EN

Stack Overflow用户
提问于 2016-03-18 15:04:51
回答 6查看 507关注 0票数 10

考虑一下这一产出:

代码语言:javascript
代码运行次数:0
运行
复制
int foo (int, char) {std::cout << "foo\n";  return 0;}
double bar (bool, double, long ) {std::cout << "bar\n";  return 3.5;}
bool baz (char, short, float) {std::cout << "baz\n";  return true;}

int main() {
    const auto tuple = std::make_tuple(5, 'a', true, 3.5, 1000, 't', 2, 5.8);
    multiFunction<2,3,3> (tuple, foo, bar, baz);  // foo  bar  baz
}

因此multiFunction<2,3,3>tuple的前2个元素传递给foo,接下来的3个tuple元素传递给bar,等等.我做到了这一点(除非函数有重载,这是一个单独的问题)。但是被调用的每个函数的返回值都会丢失。我希望这些返回值存储在某个地方,比如

代码语言:javascript
代码运行次数:0
运行
复制
std::tuple<int, double, bool> result = multiFunction<2,3,3> (tuple, foo, bar, baz);

但我不知道怎么实现。对于那些想要帮助完成这个任务的人,下面是我的(更新的)工作代码,它只将输出存储到一个字符串流中。返回所有值并不容易,特别是如果保存在流中的对象是复杂的类。

代码语言:javascript
代码运行次数:0
运行
复制
#include <iostream>
#include <tuple>
#include <utility>
#include <sstream>

template <std::size_t N, typename Tuple>
struct TupleHead {
    static auto get (const Tuple& tuple) {  // The subtuple from the first N components of tuple.
        return std::tuple_cat (TupleHead<N-1, Tuple>::get(tuple), std::make_tuple(std::get<N-1>(tuple)));
    }
};

template <typename Tuple>
struct TupleHead<0, Tuple> {
    static auto get (const Tuple&) { return std::tuple<>{}; }
};

template <std::size_t N, typename Tuple>
struct TupleTail {
    static auto get (const Tuple& tuple) {  // The subtuple from the last N components of tuple.
        return std::tuple_cat (std::make_tuple(std::get<std::tuple_size<Tuple>::value - N>(tuple)), TupleTail<N-1, Tuple>::get(tuple));
    }
};

template <typename Tuple>
struct TupleTail<0, Tuple> {
    static auto get (const Tuple&) { return std::tuple<>{}; }
};

template <typename Tuple, typename F, std::size_t... Is>
auto functionOnTupleHelper (const Tuple& tuple, F f, const std::index_sequence<Is...>&) {
    return f(std::get<Is>(tuple)...);
}

template <typename Tuple, typename F>
auto functionOnTuple (const Tuple& tuple, F f) {
    return functionOnTupleHelper (tuple, f, std::make_index_sequence<std::tuple_size<Tuple>::value>{});
}

template <typename Tuple, typename... Functions> struct MultiFunction;

template <typename Tuple, typename F, typename... Fs>
struct MultiFunction<Tuple, F, Fs...> {
    template <std::size_t I, std::size_t... Is>
    static inline auto execute (const Tuple& tuple, std::ostringstream& oss, const std::index_sequence<I, Is...>&, F f, Fs... fs) {
        const auto headTuple = TupleHead<I, Tuple>::get(tuple);
        const auto tailTuple = TupleTail<std::tuple_size<Tuple>::value - I, Tuple>::get(tuple);
    //  functionOnTuple (headTuple, f);  // Always works, though return type is lost.
        oss << std::boolalpha << functionOnTuple (headTuple, f) << '\n';  // What about return types that are void???
        return MultiFunction<std::remove_const_t<decltype(tailTuple)>, Fs...>::execute (tailTuple, oss, std::index_sequence<Is...>{}, fs...);
    }
};

template <>
struct MultiFunction<std::tuple<>> {
    static auto execute (const std::tuple<>&, std::ostringstream& oss, std::index_sequence<>) {  // End of recursion.
        std::cout << std::boolalpha << oss.str();
        // Convert 'oss' into the desired tuple?  But how?
        return std::tuple<int, double, bool>();  // This line is just to make the test compile.
    }
};

template <std::size_t... Is, typename Tuple, typename... Fs>
auto multiFunction (const Tuple& tuple, Fs... fs) {
    std::ostringstream oss;
    return MultiFunction<Tuple, Fs...>::execute (tuple, oss, std::index_sequence<Is...>{}, fs...);
}

// Testing
template <typename T> int foo (int, char) {std::cout << "foo<T>\n";  return 0;}
double bar (bool, double, long ) {std::cout << "bar\n";  return 3.5;}
template <int...> bool baz (char, short, float) {std::cout << "baz<int...>\n";  return true;}

int main() {
    const auto tuple = std::make_tuple(5, 'a', true, 3.5, 1000, 't', 2, 5.8);
    std::tuple<int, double, bool> result = multiFunction<2,3,3> (tuple, foo<bool>, bar, baz<2,5,1>);  // foo<T>  bar  baz<int...>
}
EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2016-03-18 16:12:46

这里有一种方法,可以贪婪地推导出参数的数量:

代码语言:javascript
代码运行次数:0
运行
复制
#include <tuple>

namespace detail {
    using namespace std;
    template <size_t, size_t... Is, typename Arg>
    constexpr auto call(index_sequence<Is...>, Arg&&) {return tuple<>{};}

    template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
    constexpr auto call(index_sequence<Is...>, ArgT&&, Fs&&...);

    template <size_t offset, size_t... Is,
              typename ArgT, typename F, typename... Fs,
              typename=decltype(declval<F>()(get<offset+Is>(declval<ArgT>())...))>
    constexpr auto call(index_sequence<Is...>, ArgT&& argt, F&& f, Fs&&... fs) {
        return tuple_cat(make_tuple(f(get<offset+I>(forward<ArgT>(argt))...)),
                         call<offset+sizeof...(Is)>(index_sequence<>{},
                                                    forward<ArgT>(argt),
                                                    forward<Fs>(fs)...));}

    template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
    constexpr auto call(index_sequence<Is...>, ArgT&& argt, Fs&&... fs) {
        return call<offset>(index_sequence<Is..., sizeof...(Is)>{},
                            forward<ArgT>(argt), forward<Fs>(fs)...);}
}
template <typename ArgT, typename... Fs>
constexpr auto multifunction(ArgT&& argt, Fs&&... fs) {
    return detail::call<0>(std::index_sequence<>{},
                           std::forward<ArgT>(argt), std::forward<Fs>(fs)...);}

演示。但是,由于tuple_cat是递归调用的,因此返回值的数量具有二次时间复杂度。相反,我们可以使用稍微修改过的call版本来获取每个调用的索引--然后直接获得实际的tuple

代码语言:javascript
代码运行次数:0
运行
复制
#include <tuple>

namespace detail {
    using namespace std;
    template <size_t, size_t... Is, typename Arg>
    constexpr auto indices(index_sequence<Is...>, Arg&&) {return tuple<>{};}

    template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
    constexpr auto indices(index_sequence<Is...>, ArgT&&, Fs&&...);

    template <size_t offset, size_t... Is, typename ArgT, typename F, class... Fs,
             typename=decltype(declval<F>()(get<offset+Is>(declval<ArgT>())...))>
    constexpr auto indices(index_sequence<Is...>, ArgT&& argt, F&& f, Fs&&... fs){
        return tuple_cat(make_tuple(index_sequence<offset+Is...>{}),
                         indices<offset+sizeof...(Is)>(index_sequence<>{},
                                                       forward<ArgT>(argt),
                                                       forward<Fs>(fs)...));}

    template <size_t offset, size_t... Is, typename ArgT, typename... Fs>
    constexpr auto indices(index_sequence<Is...>, ArgT&& argt, Fs&&... fs) {
        return indices<offset>(index_sequence<Is..., sizeof...(Is)>{},
                            forward<ArgT>(argt), forward<Fs>(fs)...);}

    template <typename Arg, typename F, size_t... Is>
    constexpr auto apply(Arg&& a, F&& f, index_sequence<Is...>) {
        return f(get<Is>(a)...);}

    template <typename ITuple, typename Args, size_t... Is, typename... Fs>
    constexpr auto apply_all(Args&& args, index_sequence<Is...>, Fs&&... fs) {
        return make_tuple(apply(forward<Args>(args), forward<Fs>(fs),
                          tuple_element_t<Is, ITuple>{})...);
    }
}

template <typename ArgT, typename... Fs>
constexpr auto multifunction(ArgT&& argt, Fs&&... fs) {
    return detail::apply_all<decltype(detail::indices<0>(std::index_sequence<>{},
                                                         std::forward<ArgT>(argt),
                                                         std::forward<Fs>(fs)...))>
             (std::forward<ArgT>(argt), std::index_sequence_for<Fs...>{},
              std::forward<Fs>(fs)...);}

演示2

票数 7
EN

Stack Overflow用户

发布于 2016-03-18 16:16:35

建筑从地面向上和忽略完美的转发,使我不得不键入较少。我们需要几个助手。首先,我们需要一个应用程序的部分版本,它从我们要应用到函数的元组中获取哪些索引:

代码语言:javascript
代码运行次数:0
运行
复制
<class Tuple, class F, size_t... Is>
auto partial_apply(Tuple tuple, F f, std::index_sequence<Is...>) {
    return f(get<Is>(tuple)...);
}

然后,我们需要为元组的每个子集调用该函数。假设我们已经将所有函数和索引包装在一个元组中:

代码语言:javascript
代码运行次数:0
运行
复制
template <class Tuple, class FsTuple, class IsTuple, size_t... Is>
auto multi_apply(Tuple tuple, FsTuple fs, IsTuple indexes, std::index_sequence<Is...>) {
    return std::make_tuple(
        partial_apply(tuple,
            std::get<Is>(fs),
            std::get<Is>(indexes)
            )...
        );
}

因此,在这种情况下,我们最终想要调用multi_apply(tuple, <foo,bar,baz>, <<0,1>,<2,3,4>,<5,6,7>>, <0, 1, 2>)

我们所需要知道的就是构建indexes部件。我们从<2,3,3>开始。我们需要得到部分和(<0,2,5>),并将其添加到索引序列<<0,1>,<0,1,2>,<0,1,2>>中。所以我们可以写一个部分和函数:

代码语言:javascript
代码运行次数:0
运行
复制
template <size_t I>
using size_t_ = std::integral_constant<size_t, I>;

template <class R, size_t N>
R partial_sum_(std::index_sequence<>, R, size_t_<N> ) {
    return R{};
}

template <size_t I, size_t... Is, size_t... Js, size_t S>
auto partial_sum_(std::index_sequence<I, Is...>,
        std::index_sequence<Js...>, size_t_<S> )
{
    return partial_sum_(std::index_sequence<Is...>{},
        std::index_sequence<Js..., S>{}, size_t_<S+I>{} );
}

template <size_t... Is>
auto partial_sum_(std::index_sequence<Is...> is)
{
    return partial_sum_(is, std::index_sequence<>{}, size_t_<0>{} );
};

我们可以用它作为一个元组生成所有索引:

代码语言:javascript
代码运行次数:0
运行
复制
template <size_t... Is, size_t N>
auto increment(std::index_sequence<Is...>, size_t_<N> )
{
    return std::index_sequence<Is+N...>{};
}

template <class... Seqs, size_t... Ns>
auto make_all_indexes(std::index_sequence<Ns...>, Seqs... seqs)
{
    return std::make_tuple(increment(seqs, size_t_<Ns>{})...);
}

就像这样:

代码语言:javascript
代码运行次数:0
运行
复制
template <size_t... Is, class Tuple, class... Fs>
auto multiFunction(Tuple tuple, Fs... fs)
{
    static_assert(sizeof...(Is) == sizeof...(Fs));
    return multi_apply(tuple,
        std::make_tuple(fs...),
        make_all_indexes(
            partial_sum_(std::index_sequence<Is...>{}),
            std::make_index_sequence<Is>{}...
            ),
        std::make_index_sequence<sizeof...(Is)>{}
        );

}

如果要处理void返回,只需使partial_apply返回单个元素(或空元组)的元组,并将multi_apply中的make_tuple()用法更改为tuple_cat()

票数 4
EN

Stack Overflow用户

发布于 2016-03-18 17:37:20

这里还有另一个难题:

代码语言:javascript
代码运行次数:0
运行
复制
template<std::size_t N>
constexpr Array<std::size_t, N> scan(std::size_t const (&a)[N])
{
    Array<std::size_t, N> b{};
    for (int i = 0; i != N - 1; ++i)
        b[i + 1] = a[i] + b[i];
    return b;
}

template<std::size_t O, std::size_t... N, class F, class Tuple>
inline decltype(auto) eval_from(std::index_sequence<N...>, F f, Tuple&& t)
{
    return f(std::get<N + O>(std::forward<Tuple>(t))...);
}

template<std::size_t... O, std::size_t... N, class Tuple, class... F>
inline auto multi_function_impl1(std::index_sequence<O...>, std::index_sequence<N...>, Tuple&& t, F... f)
{
    return pack(eval_from<O>(std::make_index_sequence<N>(), f, std::forward<Tuple>(t))...);
}

template<std::size_t... I, std::size_t... N, class Tuple, class... F>
inline auto multi_function_impl0(std::index_sequence<I...>, std::index_sequence<N...>, Tuple&& t, F... f)
{
    constexpr std::size_t ns[] = {N...};
    constexpr auto offsets = scan(ns);
    return multi_function_impl1(std::index_sequence<offsets[I]...>(), std::index_sequence<N...>(), std::forward<Tuple>(t), f...);
}

template<std::size_t... N, class Tuple, class... F>
auto multi_function(Tuple&& t, F... f)
{
    return multi_function_impl0(std::make_index_sequence<sizeof...(N)>(), std::index_sequence<N...>(), std::forward<Tuple>(t), f...);
}

其中packArray分别类似于std::make_tuplestd::array,但是为了克服一些问题:

  • std::make_tuple破坏了它的args,因此失去了引用。
  • std::array不能用c++14中的参数写它的标记

演示

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36087785

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档