带维数的多维数组模板

sz *_*ter 6 c++ templates multidimensional-array c++17 deduction-guide

我想制作一个NDArray具有固定尺寸的模板,但可以在每个尺寸上调整大小。

我的问题是如何让它能够根据使用了多少对来推断构造函数中的尺寸{}?构造函数中的元素将用于初始化一些元素。

#include <array>
#include <iostream>

template<typename T, size_t Dimension>
class NDArray
{
    T* buffer = nullptr; //flattened buffer for cache locality
    std::array<size_t, Dimension> dimension;    //keep the current sizes of each dimension
public:
    NDArray(std::initializer_list<T> elements) : dimension{elements.size()}   //for 1D
    {
        std::cout << "Dimension = " << Dimension << '\n';
    }
    NDArray(std::initializer_list<NDArray<T, Dimension-1>> list) //how to make this works???
    {
        std::cout << "Dimension = " << Dimension << '\n';
    }
};

template<typename T, size_t N>
NDArray(const T(&)[N]) -> NDArray<T, 1>;

int main()
{
    NDArray a{ {3,4,5} };//OK, NDArray<int, 1> because of the deduction guide
    NDArray b{ {{1,2,3}, {4,5,6}} };//Nope, I want: NDArray<int, 2>
}
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 4

这在一般情况下是不可能的,但在您想要阐明的许多特定情况下是可能的。

\n

初始化列表没有类型。可以推断出它的类型的唯一方法(例如,与默认模板参数分开)是我们在[temp.deduct.call]/1中阐明了两种特殊情况:

\n
\n

如果从中删除引用和 cv 限定符给出P\ std::initializer_list<P\xe2\x80\xb2>nor P\xe2\x80\xb2[N]for someP\xe2\x80\xb2并且N参数是非空初始值设定项列表 ([dcl.init.list]),则对初始值设定项列表的每个元素独立执行推导,取P\xe2\x80\xb2为单独的函数模板参数类型和第一个初始化元素作为相应的参数。在这种情况下,如果是非类型模板参数,则从初始值设定项列表的长度推导出来。否则,初始值设定项列表参数会导致该参数被视为非推导上下文 ([temp.deduct.type])。P\xe2\x80\xb2iiP\xe2\x80\xb2[N]NN

\n
\n

这是让以下工作有效的规则:

\n
template <typename T>\nconstexpr auto f(std::initializer_list<T>) -> int { return 1; }\n\nstatic_assert(f({1, 2, 3}) == 1);\n
Run Code Online (Sandbox Code Playgroud)\n

但这还不足以让它发挥作用:

\n
static_assert(f({{1, 2}, {3, 4}}) == 1); // ill-formed (no matching call to f)\n
Run Code Online (Sandbox Code Playgroud)\n

因为规则是 - 好吧,我们可以剥离一层,initializer_list但随后我们必须推导出元素。一旦我们删除一层初始化列表,我们就会尝试T从中进行推断{1, 2},但结果会失败 - 我们不能这样做。

\n

但我们知道如何从中推断出一些东西{1, 2}——这就是同样的规则。我们只需要再做一次:

\n
template <typename T>\nconstexpr auto f(std::initializer_list<T>) -> int { return 1; }\n\ntemplate <typename T>\nconstexpr auto f(std::initializer_list<std::initializer_list<T>>) { return 2; }\n\n\nstatic_assert(f({1, 2, 3}) == 1);\nstatic_assert(f({{1, 2}, {3, 4}}) == 2);\n
Run Code Online (Sandbox Code Playgroud)\n

然后再次:

\n
template <typename T>\nconstexpr auto f(std::initializer_list<T>) -> int { return 1; }\n\ntemplate <typename T>\nconstexpr auto f(std::initializer_list<std::initializer_list<T>>) { return 2; }\n\ntemplate <typename T>\nconstexpr auto f(std::initializer_list<std::initializer_list<std::initializer_list<T>>>) { return 3; }\n\n\nstatic_assert(f({1, 2, 3}) == 1);\nstatic_assert(f({{1, 2}, {3, 4}}) == 2);\nstatic_assert(f({{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}}) == 3);\n
Run Code Online (Sandbox Code Playgroud)\n

就像我们对 进行剥离一样std::initializer_list<T>,我们也对 进行剥离T[N]。其工作方式相同,只是少输入一点:

\n
template <typename T, size_t N>\nconstexpr auto f(T(&&)[N]) -> int { return 1; }\n\ntemplate <typename T, size_t N1, size_t N2>\nconstexpr auto f(T(&&)[N1][N2]) { return 2; }\n\ntemplate <typename T, size_t N1, size_t N2, size_t N3>\nconstexpr auto f(T(&&)[N1][N2][N3]) { return 3; }\n\n\nstatic_assert(f({1, 2, 3}) == 1);\nstatic_assert(f({{1, 2}, {3, 4}}) == 2);\nstatic_assert(f({{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}}) == 3);\n
Run Code Online (Sandbox Code Playgroud)\n