是否有一个元组for_each()返回从调用的函数返回的所有值的元组?

时间:2016-12-17 14:39:43

标签: c++ foreach tuples c++17

几个月前我正在查看this tuple for_each() implementation,并且想知道是否有可能实现一个版本来收集调用函数的返回值作为结果?

我想在我的代码库中执行此操作的原因我有以下函数,它接受一个可变的形状列表输入,并返回一个值元组。

template <typename... T, typename... R>
static constexpr auto map_to_opengl(T &&... shapes)
{
  return std::make_tuple(shape_mapper::map_to_array_floats(shapes)...);
}

好吧,我想更改我的函数签名以接受形状元组,并返回在每个形状上调用函数的结果(这应该在语义上等同于上面的代码)。如果我能做到这一点,我可以让我的代码更干,这对我来说很重要。

template <typename... T, typename... R>
static constexpr auto map_to_opengl(std::tuple<T...> &&shapes)
{
  return tuple_foreach(shapes, &shape_mapper::map_to_array_floats);
}

tuple_foreach的实施不允许收集任何值。是否可以编写这样的功能?如果它存在于Hana中,我就错过了它:(

我猜你不会把这个算法称为for_each,但可能会积累?我不确定。

3 个答案:

答案 0 :(得分:4)

使用std::apply执行此操作将是以下几点:

template<typename T, typename F>
constexpr auto map_tuple_elements(T&& tup, F f) {
    return std::apply([&f](auto&&... args){
               return std::make_tuple(f(decltype(args)(args))...);    
           }, std::forward<T>(tup));
}

答案 1 :(得分:3)

C ++ 17中的标准库中没有添加任何特别有用的东西(我能想到);这是通常的C ++ 14方法(使用包扩展)作为独立算法:

namespace detail {
    template<typename T, typename F, std::size_t... Is>
    constexpr auto map_tuple_elements(T&& tup, F& f, std::index_sequence<Is...>) {
        return std::make_tuple(f(std::get<Is>(std::forward<T>(tup)))...);
    }
}

template<typename T, typename F, std::size_t TupSize = std::tuple_size_v<std::decay_t<T>>>
constexpr auto map_tuple_elements(T&& tup, F f) {
    return detail::map_tuple_elements(
        std::forward<T>(tup), f,
        std::make_index_sequence<TupSize>{}
    );
}

Online Demo

答案 2 :(得分:0)

以下main()构造一个元组,将一个函数应用于元组中的每个值,并生成另一个元组。你可以将整个事物简单地包装成自己的函数。

此解决方案使用了一些C ++ 17新增的模板,但如果需要,它们都可以为C ++ 14重新实现:

#include <utility>
#include <tuple>
#include <iostream>

// The function

int square(int n)
{
    return n * 2;
}

// The helper class for unpacking a tuple into a parameter pack,
// invoking square(), then packing the result back into a tuple.

template<typename tuple, typename index_sequence> class apply_square;

template<typename tuple,
     size_t... sequence>
class apply_square<tuple, std::index_sequence<sequence...>> {

public:

    template<typename tuple_arg>
    static auto do_apply_square(tuple_arg &&tupple)
    {
        return std::make_tuple(square(std::get<sequence>(tupple))...);
    }
};

int main()
{
    // Create a sample tuple

    auto f=std::make_tuple(1, 2);

    // Invoke appropriately-specialized do_apply_square() against
    // the tuple.

    typedef std::make_index_sequence<std::tuple_size<decltype(f)>
                     ::value> tuple_indexes;

    auto f_squared=apply_square<decltype(f), tuple_indexes>
        ::do_apply_square(f);

    // f_squared should now be another tuple. Let's see:

    int &first=std::get<0>(f_squared);
    int &second=std::get<1>(f_squared);

    std::cout << first << ' ' << second << std::endl;
}