代码之家  ›  专栏  ›  技术社区  ›  Dietmar Kühl

std::tuple访问算法中的折叠表达式[duplicate]

  •  0
  • Dietmar Kühl  · 技术社区  · 4 年前

    std::tuple 需要将操作应用于 std::tuple "zip iterator" 底层范围和迭代器存储在 std::tuple std::index_sequence<std::tuple_size<Tuple>> 为了得到相关的元素,它似乎使用了在 std::tuple std::tuple -类似结构(例如:。, std::array std::pair )可以改进代码。

    tuple_for_each() std::index_sequence 要应用这些元素:

    template <typename Tuple, typename Fun>
    void tuple_for_each(Tuple&& tuple, Fun fun)
    {
        auto const impl = [&tuple, fun]<std::size_t...I>(std::index_sequence<I...>){
            (fun(std::get<I>(tuple)), ...);
        };
        impl(std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>());
    }
    

    当操作不是真正的元素智能化时 for_each transform accumulate inner_product .在自定义设置中,这些可以很容易地编写,例如:。,

    template <typename... T>
    struct some_struct {
        std::tuple<T...> tuple;
    
        template <typename Tuple, std::size_t... I>
        static bool equals(Tuple&& t0, Tuple&& t1, std::index_sequence<I...>) {
            return ((std::get<I>(t0) == std::get<I>(t1)) && ...);
        }
        bool operator== (some_struct const& other) const {
            return equals(this->tuple, other.tuple, std::make_index_sequence<sizeof...(T)>());
        }
    };
    

    operator==() 可以委托给合适的 tuple 算法:

    bool operator== (some_struct const& other) const  {
        return tuple_inner_product(this->tuple, other.tuple, true, std::equal_to<>(), std::logical_and<>());
    }
    

    tuple_inner_product 使用折叠表达式,而不是基于最后一个参数的类型创建特殊版本。我知道如何实现递归版本,但我不认为如果合并操作是逻辑操作数之一,该版本会缩短求值过程(在调用函数之前,始终需要确定函数参数):

    template <typename T0, typename T1, typename Init, typename Transform, typename Combine>
    auto tuple_inner_product(T0&& t0, T1&& t1, Init init, Transform transform, Combine combine) {
        auto const recurse = [&t0, &t1, transform, combine]<std::size_t I>(
                                   std::integral_constant<std::size_t, I>,
                                   auto const& r, auto init) {
            if constexpr (I == std::min(std::tuple_size_v<std::decay_t<T0>>,
                                        std::tuple_size_v<std::decay_t<T1>>)) {
                return init;
            }
            else {
                return combine(transform(std::get<I>(t0), std::get<I>(t1)),
                               r(std::integral_constant<std::size_t, I+1>(), r, init));
            }
        };
        return recurse(std::integral_constant<std::size_t, 0u>(), recurse, init);
    }
    

    因此,问题变成了是否有一种方法可以使用折叠表达式来实现该算法?

    1 回复  |  直到 4 年前
        1
  •  1
  •   Yakk - Adam Nevraumont    4 年前

    template<TupleLike Tuple>
    constexpr auto tuple_indexes(Tuple&&);
    

    这就是 std::tuple<std::integral_constant<std::size_t,0>, std::integral_constant<std::size_t,1>, ..., std::integral_constant<std::size_t,N-1> 一个元组。

    现在我们得到:

    bool operator== (some_struct const& other) const  { 
      return std::apply([&](auto...Is){
        return (true &&... && (std::get<Is>(this->tuple)==std::get<Is>(other.tuple)));
      }, tuple_indexes(this->tuple));
    }
    

    我还发现一个词汇功能非常有用:

    template<std::size_t N>
    using index_t=std::integral_constant<std::size_t,N>;
    template<std::size_t N>
    constexpr index_t<N> index={};
    
    template<std::size_t...Is>
    auto indexer_for(std::index_sequence<Is...>){
      return [](auto&& f)->decltype(auto){ return f(index<Is>...); };
    }
    template<std::size_t N>
    auto indexer_upto(){
      return indexer_for(std::make_index_sequence<N>{});
    }
    

    这可以用很多方法。例如

    bool operator== (some_struct const& other) const  { 
      return indexer_upto<sizeof...Ts>()([&](auto...Is){
        return (true &&... && (std::get<Is>(this->tuple)==std::get<Is>(other.tuple)));
      });
    }
    

    比我上面的索引元组更有效、更通用。