【问题标题】:Use two fold expressions in one line在一行中使用两个折叠表达式
【发布时间】:2017-11-29 09:48:02
【问题描述】:

我正在学习 C++ 折叠表达式,它们非常适合扩展一个参数包。但是,如果我必须在一次调用中扩展其中的几个呢?

我准备了一个例子来说明我的意思:

#include <cstddef>
#include <iostream>
#include <utility>

template<
    template<size_t ix_, size_t iy_> typename Functor,
    typename FunctorContext,
    size_t... ix,
    size_t... iy>
void RepeatImpl(
    FunctorContext* context,
    std::index_sequence<ix...>,
    std::index_sequence<iy...>)
{
    (Functor<ix, iy>::Execute(context), ...);
}

template<
    template<size_t ix_, size_t iy_> typename Functor,
    size_t width, size_t height,
    typename FunctorContext>
void Repeat(FunctorContext* context)
{
    RepeatImpl<Functor>(
        context,
        std::make_index_sequence<width>(),
        std::make_index_sequence<height>());
}

template<size_t ix, size_t iy>
struct Init2dArrayFunctor
{
    template<typename T>
    static void Execute(T* array)
    {
        array[iy][ix] = 10;
    }
};

int main(int, const char**)
{
    constexpr size_t w = 10;
    constexpr size_t h = 10;
    int array[w][h] { 0 };
    Repeat<Init2dArrayFunctor, w, h>(array);

    for(size_t iy = 0; iy < h; ++iy)
    {
        for(size_t ix = 0; ix < w; ++ix)
        {
            std::cout << array[iy][ix] << ' ';
        }

        std::cout << std::endl;
    }

    return 0;
}

我的问题是关于这一行的:

(Functor<ix, iy>::Execute(context), ...);

在这个特定的示例中,它将扩展到这些调用:

Functor<0, 0>::Execute(context)
Functor<1, 1>::Execute(context)
Functor<2, 2>::Execute(context)
...

但我需要它来调用 ixiy 包的所有组合:

Functor<0, 0>::Execute(context)
Functor<0, 1>::Execute(context)
Functor<0, 2>::Execute(context)
...
Functor<1, 0>::Execute(context)
Functor<1, 1>::Execute(context)
Functor<1, 2>::Execute(context)
...

我知道我可以做一个额外的层来遍历行和列(然后在不同的方法中使用两个折叠表达式):

#include <cstddef>
#include <iostream>
#include <utility>


template<
    template<size_t ix_, size_t iy_> typename Functor,
    size_t iRow,
    typename FunctorContext,
    size_t... iColumn>
void RepeatImpl_Row(
    FunctorContext* context,
    std::index_sequence<iColumn...>)
{
    (Functor<iColumn, iRow>::Execute(context), ...);
}

template<
    template<size_t ix_, size_t iy_> typename Functor,
    size_t columns,
    typename FunctorContext,
    size_t... iRow>
void RepeatImpl(
    FunctorContext* context,
    std::index_sequence<iRow...>)
{
    (RepeatImpl_Row<Functor, iRow>(context, std::make_index_sequence<columns>()), ...);
}

template<
    template<size_t ix_, size_t iy_> typename Functor,
    size_t width, size_t height,
    typename FunctorContext>
void Repeat(FunctorContext* context)
{
    RepeatImpl<Functor, width>(
        context,
        std::make_index_sequence<height>());
}

template<size_t ix, size_t iy>
struct Init2dArrayFunctor
{
    template<typename T>
    static void Execute(T* array)
    {
        array[iy][ix] = 10;
    }
};

int main(int, const char**)
{
    constexpr size_t w = 10;
    constexpr size_t h = 10;
    int array[w][h] { 0 };
    Repeat<Init2dArrayFunctor, w, h>(array);

    for(size_t iy = 0; iy < h; ++iy)
    {
        for(size_t ix = 0; ix < w; ++ix)
        {
            std::cout << array[iy][ix] << ' ';
        }

        std::cout << std::endl;
    }

    return 0;
}

但是这种方式使代码更难阅读。 也许可以在一行中使用两个折叠表达式来完成这个技巧?

【问题讨论】:

    标签: c++ c++17 fold


    【解决方案1】:

    你可以简单地写如下:

    template<
        size_t height,
        template<size_t ix_, size_t iy_> typename Functor,
        typename FunctorContext,
        size_t... ixy>
    void RepeatImpl(
        FunctorContext* context,
        std::index_sequence<ixy...>)
    {
        (Functor< ixy / height, ixy % height >::Execute(context), ...);
    }
    
    template<
        template<size_t ix_, size_t iy_> typename Functor,
        size_t width, size_t height,
        typename FunctorContext>
    void Repeat(FunctorContext* context)
    {
        RepeatImpl<height,Functor>(
            context,
            std::make_index_sequence<width*height>() );
    }
    

    当我们有可变参数类型而不是索引时它会起作用吗?

    在这种情况下,只需将类型列表包装在可索引的类型元组中。

    但是有没有办法通过折叠来处理一些可变参数?

    到目前为止,不,我认为这是不可能的;包总是线性扩展,如果存在多个包,它们会同时扩展并且必须具有相同的长度,正如您已经观察到的。我们没有对“power-product”包扩展的“直接”语言支持。如果你想要一对模板参数的笛卡尔积,你总是会得到类似上面的东西......

    【讨论】:

    • 此解决方案仅适用于此合成示例。例如,当我们有可变参数类型而不是索引时,它将不起作用。我准备了带有索引的示例,因为我认为这是解释我的问题的最简单的示例。
    • @КонстантинЛазукин 它会起作用,只需将类型列表包装在可索引的类型元组中......
    • 很好,这是不错的选择。但是有没有办法通过折叠处理一些可变参数?对于这些烦人的问题,我感到很抱歉,但我的意思是找出使用这个新的 c++ 功能我可以做些什么。
    猜你喜欢
    • 2020-10-31
    • 1970-01-01
    • 2021-10-20
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多