【问题标题】:Equivalent of std::transform for tuples元组的 std::transform 等价物
【发布时间】:2015-04-23 10:01:39
【问题描述】:

我想要一个函数,其行为类似于元组的std::transform。基本上要实现的功能是

template<size_t From, size_t To, class Tuple, class Func>
void tuple_transform(Tuple&& source, Tuple&& target, Func f)
{
    // elements <From, To> of `target` ti become `f(si)`, where
    // si is the corresponding element of `source`
};

我相信要实现这一点,我需要一个 编译时整数范围 结构,它是std::index_sequence 的泛化,我已经用cti::range 实现了here。我也相信this 类型的编译时间遍历在这里是理想的:

template<class Func, class Tuple, size_t...Is>
void for_each_in_tuple(Func f, Tuple&& tuple, std::index_sequence<Is...>){
    using expander = int[];
    (void)expander { 0, ((void)f(std::get<Is>(std::forward<Tuple>(tuple))), 0)... };
}

template<class Func, class Tuple>
void for_each_in_tuple(Func f, Tuple&& tuple){
    for_each_in_tuple(f, std::forward<Tuple>(tuple),
               std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
}

任何人都可以帮助我实施吗?


注意事项:关于变异函数的类型

@MohitJain 就像在链接的代码 (tuple_transform) 中一样,没有考虑到这一点(Func 具有单一类型)。如果这样解决了,我可以通过传递模板模板参数template&lt;class&gt; class Func 轻松扩展它,并要求我的转换类型是这样的

template<typename T>
struct Func
{
    static void apply(T& val) { ... }
}

然后在元组变换的主体内,每个变换函数可以这样调用:

get<I>(target) = func<typename tuple_element<I, Tuple>::type>::apply(get<I>(source))

编辑

刚刚在 accu 2015 上进行了一次闪电演讲。以上是演示结束时的 CodeKata。我将留下here 的演示文稿,希望它能对任何实施尝试有所帮助(我认为几乎所有需要的工具都已提供,因此我们将对此进行更多尝试)

【问题讨论】:

  • std::transform 中每个元素的类型都是相同的。在std::tuple 中,此类型可能不同。你打算如何用 1 个变换函数来变换它们?是否重载、模板化?

标签: c++ tuples variadic-templates template-meta-programming


【解决方案1】:

这是一个使用来自hereindex_range 的解决方案。

template<size_t SN, size_t DN, class TSrc, class TDest, class Func>
void tuple_call_assign(TSrc&& source, TDest& target, Func f)
{
    std::get<DN>(target) = f(std::get<SN>(std::forward<TSrc>(source))); 
}

template<size_t From, size_t To, class TSrc, class TDest, class Func, size_t...Is, size_t...DIs>
void tuple_transform(TSrc&& source, TDest& target, Func f,
                     std::index_sequence<Is...>, std::index_sequence<DIs...>)
{
    using expander = int[];
    (void)expander { 0, (tuple_call_assign<Is,DIs>(std::forward<TSrc>(source),target,f), 0)... };
}

template<size_t From, size_t To, size_t FromDest, class TSrc, class TDest, class Func>
void tuple_transform(TSrc&& source, TDest& target, Func f)
{
    static_assert(To > From, "Range must be increasing");
    static_assert(To <= std::tuple_size<std::decay_t<TSrc>>::value+1, 
        "Range must be valid for source tuple");
    constexpr size_t RangeSize = To-From;
    static_assert(FromDest+RangeSize <= std::tuple_size<std::decay_t<TDest>>::value, 
        "Range must be valid for target tuple");

    tuple_transform<From,To>(std::forward<TSrc>(source), target, f,
                    index_range<From,To>(), index_range<FromDest, FromDest+RangeSize>());
}

Demo

这需要第三个模板参数来指定要转换为目标元组的起始索引。

【讨论】:

    【解决方案2】:

    index_sequence 解决方案已经提到过:

    template <std::size_t From, size_t... indices, typename T1, typename T2, typename Func>
    void transform(T1&& s, T2& t, Func f, std::index_sequence<indices...>)
    {
        (void)std::initializer_list<int>{
            (std::get<indices+From>(t) = f(std::get<indices>(std::forward<T1>(s))), 0)...};  
    }
    
    
    template <std::size_t From, std::size_t To, typename T1, typename T2, typename Func>
    void transform(T1&& s, T2& t, Func f)
    {
        transform<From>(std::forward<T1>(s), t, f, std::make_index_sequence<To-From+1>());
    }
    

    Demo.

    【讨论】:

    • 这个怎么实现,当Func需要做一个函数模板的时候?
    猜你喜欢
    • 2013-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-21
    • 2015-11-03
    • 1970-01-01
    • 2022-01-24
    • 1970-01-01
    相关资源
    最近更新 更多