首页 > 解决方案 > 是否可以使用可变参数模板参数来初始化多维容器?

问题描述

我正在尝试使用可变参数模板和构造函数参数来初始化自定义类中的多维数组的值ArrayND。到目前为止,我已经成功地Array用 multi Dimension 初始化了任意维度的实例std::arrays,但是初始化主要但并不总是需要的双支撑std::array变得有点难看并且难以解析,例如:

constinit ArrayND<5, 4, 6> my_nd_array({
    {
        {{ {0.4f}, {0.6f}, {0.1f}, {0.4f} }},
        {0.4f, 0.6f, 0.1f, 0.4f},
        {0.4f, 0.6f, 0.1f, 0.4f},
        {0.4f, 0.6f, 0.1f, 0.4f},
        {0.4f, 0.6f, 0.1f, 0.4f},
    }
});

到目前为止,我可以毫无困难地初始化 Array 类的一维实现,尽管我宁愿在这个实例中的值周围需要一组花括号:

template<size_t SIZE>
struct Array1D {

    template<class ... VALUES>
    constexpr explicit Array1D(const VALUES ... values)
            : data({values...})
    {}

    std::array<float, SIZE> data;
};

constinit Array1D<2> my_2_array(
        0.1f, 0.2f
);

但我不确定是否/如何做类似的事情来干净地初始化高维数组。我一直在尝试使用 Array2D,但是我在下面粘贴的初始化版本都不起作用:

template<size_t SIZE_0, size_t SIZE_1>
struct Array2D {

    template<class ... VALUES>
    constexpr explicit Array2D(const VALUES ... values)
            : data(values...)
    {}

    std::array<std::array<float, SIZE_1>, SIZE_0> data;
};

// ERROR: No matching constructor...
constinit Array2D<2, 2> my_2x2_array_A(
        { 0.1f, 0.2f }, { 0.3f, 0.4f }
);

// ERROR: No matching constructor...
constinit Array2D<2, 2> my_2x2_array_B(
        { { 0.1f, 0.2f }, { 0.3f, 0.4f } }
);

然而,最终的目标是能够用我的 ArrayND 做同样的事情,它看起来像这样:

template<size_t SIZE, size_t ... SUB_SHAPE>
struct ArrayND {

    template<class ... VALUES>
    constexpr explicit ArrayND(const VALUES ... values)
            : data(values...)
    {}

    using DataType = std::array<typename ArrayND<SUB_SHAPE...>::DataType, SIZE>;
    DataType data;
};

有人对如何在不传递多维的情况下实现这样的目标有任何想法std::array吗?

为了澄清,constexpr构造函数对我需要做的事情很重要。我有一个std::vector运行良好的版本,但在 clang 13.0 中std::vector仍然没有consexpr构造函数(而且我不确定它是否被标准接受,可能只有 MSVC 实现了这个功能......)。

编辑:

可能值得注意的是,在非缩减实现中,这些数组都将派生自最终的 ND 类,并专门针对标量和一维版本。Array1D 和 Array2D 类仅用于以更简单的形式测试此问题的解决方案。

更新:

如果可以避免的话,我宁愿不为 C 数组管理和编写算法,但我突然想到,我至少没有尝试过将它们用于构造......但我正在努力让它发挥作用出色地:

template<size_t SIZE_0, size_t SIZE_1>
struct Array2D {

    constexpr explicit Array2D(const float values[SIZE_0][SIZE_1])
            : data(values)
    {}

    float data[SIZE_0][SIZE_1];
};

// ERROR: No matching constructor...
constinit Array2D<2, 2> my_2x2_array_0(
        {{ 0.1f, 0.2f }, { 0.3f, 0.4f }}
);

// ERROR: No viable conversion from 'float[2][2]' to 'Array2D<2, 2>'. Explicit constructor is not a candidate
// Personal note: It appears to be trying to use the copy constructor, which I guess kind of makes sense
constinit float raw_data[2][2] = {{ 0.1f, 0.2f }, { 0.3f, 0.4f }};
constinit Array2D<2, 2> my_2x2_array_1(raw_data);

标签: c++templatesvariadic-templatesc++20class-template

解决方案


std::array确实需要双括号,因为它使用聚合初始化。

你可以创建你的版本,它有一个带有 N 个参数的构造函数:

// helper used for variadic expension
template <std::size_t, typename T> using always_t = T;

template <typename T, typename Seq> struct my_array_impl;

template <typename T, std::size_t... Is>
struct my_array_impl<T, std::index_sequence<Is...>>
{
    // Here, constructor is not template <typename ... Ts>
    // {..} has no type, and can only be deduced
    // to some types we are not interested in
    // We use always_t<Is, T> trick to have constructor similar to
    // my_array_impl(T arg1, T arg2, .., T argN)
    constexpr my_array_impl(always_t<Is, T>... args) : data{{args...}} {}

    // ...

    std::array<T, sizeof...(Is)> data;
};

template <typename T, std::size_t N>
using my_array = my_array_impl<T, std::make_index_sequence<N>>;

接着

// just compute type alias currently
// to have my_array<my_array<..>, Size>
template <typename T, std::size_t I, std::size_t ... Is>
struct ArrayND_impl<T, I, Is...>
{
    using DataType = my_array<typename ArrayND_impl<T, Is...>::DataType, I>;
};

template <typename T, std::size_t ... Sizes>
using ArrayND = typename ArrayND_impl<T, Sizes...>::DataType;

用法类似于:

template <std::size_t SIZE_0, std::size_t SIZE_1, std::size_t SIZE_2>
using Array3D = ArrayND<float, SIZE_0, SIZE_1, SIZE_2>;

constinit Array3D<2,2,2> arr(
    {
        {1, 2},
        {3, 4},
    },
    {
        {5, 6},
        {7, 8},
    }
);

演示


推荐阅读