【问题标题】:Is there a tuple for_each() that returns a tuple of all values returned from the functions invoked?是否有一个元组 for_each() 返回从调用的函数返回的所有值的元组?
【发布时间】:2016-12-17 14:39:43
【问题描述】:

几个月前我浏览了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,但可能会累积?我不确定这里。

【问题讨论】:

  • 您可以将现有代码与新的std::apply结合起来?
  • 也许,但如果我要对每个函数调用 apply ,我不确定如何将值“收集”到一个元组中,因为每个函数都是一个一个调用的,我不知道我在哪里将放置中间返回值。
  • 在术语方面,FP 语言将其称为“地图”,C++ 将其称为“变换”。
  • 确实,您正在寻找hana::transform

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


【解决方案1】:

使用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));
}

【讨论】:

    【解决方案2】:

    在 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

    【讨论】:

    • 这是一个非常优雅的解决方案,我的朋友。带有示例的演示使这个答案成为传奇。谢谢!!你提到“通常的方法”,我真的很想了解更多。你有什么资料可以让我了解更多关于“常用方法”的信息吗?
    • @Short :我建议阅读std::integer_sequence and friends,并阅读一些使用它们的高投票答案(std::index_sequence 可能是要搜索的符号)。
    • 不错的解决方案! @ildjarn 为什么 tuple_size 需要 std::decay?
    • @fen : 因为T&amp;&amp;是一个转发引用,当传入一个左值时T将是tuple&lt;...&gt;&amp;tuple&lt;...&gt; const&amp;;但是std::tuple_size 只专门用于tuple&lt;...&gt;,所以我们必须去掉引用和可能的常量。在 C++20 添加 std::remove_cvref_t 之前,使用 decay_t 是一种简单的(如果过度杀伤)解决方案。
    • 感谢@ildjarn 的解释!有道理!
    【解决方案3】:

    下面的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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-06-21
      • 2021-07-30
      • 2016-08-20
      • 1970-01-01
      • 2014-04-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多