在 C++ 中生成任意嵌套的向量 [英] Generate Arbitrarily Nested Vectors in C++

查看:36
本文介绍了在 C++ 中生成任意嵌套的向量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个函数,以便生成任意嵌套的向量并使用 C++ 中的给定特定值进行初始化.例如, auto test_vector = n_dim_vector_generator<2, long double>(static_cast(1), 1); 预计会创建一个类型为 std 的test_vector"对象::vector>.这个test_vector的内容应该和下面的代码一样.

I am trying to write a function in order to generate arbitrarily nested vectors and initialize with the given specific value in C++. For example, auto test_vector = n_dim_vector_generator<2, long double>(static_cast<long double>(1), 1); is expected to create a "test_vector" object which type is std::vector<std::vector<long double>>. The content of this test_vector should as same as the following code.

    std::vector<long double> vector1;
    vector1.push_back(1);
    std::vector<std::vector<long double>> test_vector;
    test_vector.push_back(vector1);

n_dim_vector_generator 函数更复杂的用法:

The more complex usage of the n_dim_vector_generator function:

auto test_vector2 = n_dim_vector_generator<15, long double>(static_cast<long double>(2), 3);

在这种情况下,参数static_cast(2)是向量中的数据,数字3是推送次数.所以,这个test_vector2的内容应该和下面的代码一样.

In this case, the parameter static_cast<long double>(2) is as the data in vectors and the number 3 is as the push times. So, the content of this test_vector2 should as same as the following code.

    std::vector<long double> vector1;
    vector1.push_back(static_cast<long double>(2));
    vector1.push_back(static_cast<long double>(2));
    vector1.push_back(static_cast<long double>(2));
    std::vector<std::vector<long double>> vector2;
    vector2.push_back(vector1);
    vector2.push_back(vector1);
    vector2.push_back(vector1);
    std::vector<std::vector<std::vector<long double>>> vector3;
    vector3.push_back(vector2);
    vector3.push_back(vector2);
    vector3.push_back(vector2);
    //...Totally repeat 15 times in order to create test_vector2
    std::vector<...std::vector<long double>> test_vector2;
    test_vector2.push_back(vector14);
    test_vector2.push_back(vector14);
    test_vector2.push_back(vector14);

n_dim_vector_generator 函数的具体实现如下.

The detail to implement n_dim_vector_generator function is as follows.

#include <iostream>
#include <vector>

template <typename T, std::size_t N>
struct n_dim_vector_type;

template <typename T>
struct n_dim_vector_type<T, 0> {
    using type = T;
};

template <typename T, std::size_t N>
struct n_dim_vector_type {
    using type = std::vector<typename n_dim_vector_type<T, N - 1>::type>;
};


template<std::size_t N, typename T>
typename n_dim_vector_type<T,N>::type n_dim_vector_generator(T t, unsigned int);

template <std::size_t N, typename T>
typename n_dim_vector_type<T, N>::type n_dim_vector_generator<N, T>(T input_data, unsigned int push_back_times) {
    if (N == 0)
    {
        return std::move(input_data);
    }
    typename n_dim_vector_type<T, N>::type return_data;
    for (size_t loop_number = 0; loop_number < push_back_times; loop_number++)
    {
        return_data.push_back(n_dim_vector_generator<N - 1, T>(input_data, push_back_times));
    }
    return return_data;
}

结果,我得到一个错误 'return': cannot convert from 'long double' to 'std::vector>,std::allocator<std::vector<long double,std::allocator<long double>>>>' 我知道它是由 if (N == 0) 块作为递归结构的终止条件.但是,如果我尝试将终止条件编辑为单独的形式.

As a result, I got an error 'return': cannot convert from 'long double' to 'std::vector<std::vector<long double,std::allocator<long double>>,std::allocator<std::vector<long double,std::allocator<long double>>>>' I know that it caused by if (N == 0) block which is as the terminate condition to recursive structure. However, if I tried to edit the terminate condition into separate form.

template <typename T>
inline T n_dim_vector_generator<0, T>(T input_data, unsigned int push_back_times) {
    return std::move(input_data);
}

template <std::size_t N, typename T>
typename n_dim_vector_type<T, N>::type n_dim_vector_generator<N, T>(T input_data, unsigned int push_back_times) {
    typename n_dim_vector_type<T, N>::type return_data;
    for (size_t loop_number = 0; loop_number < push_back_times; loop_number++)
    {
        return_data.push_back(n_dim_vector_generator<N - 1, T>(input_data, push_back_times));
    }
    return return_data;
}

发生错误'n_dim_vector_generator':非法使用显式模板参数.这个问题有没有更好的解决方案?

The error 'n_dim_vector_generator': illegal use of explicit template arguments happened. Is there any better solution to this problem?

开发环境为 Windows 10 1909 和 Microsoft Visual Studio Enterprise 2019 版本 16.4.3

The develop environment is in Windows 10 1909 with Microsoft Visual Studio Enterprise 2019 Version 16.4.3

推荐答案

实现你的具体映射:

auto test_vector = n_dim_vector_generator<2, long double>(2, 3)

对于填充为 2 的 3x3 向量,如果您利用这个 vector 构造函数,您的模板可以更简单一些:

to a 3x3 vector filled with 2's, your template can be a bit simpler if you take advantage of this vector constructor:

std::vector<std::vector<T>>(COUNT, std::vector<T>(...))

由于 vector 是可复制的,这将用矢量的不同副本填充 COUNT 个插槽.所以...

Since vector is copyable, this will fill COUNT slots with a different copy of the vector. So...

template <size_t N, typename T>
struct n_dim_vector_generator {
    using type = std::vector<typename n_dim_vector_generator<N-1, T>::type>;
    type operator()(T value, size_t size) {
        return type(size, n_dim_vector_generator<N-1, T>{}(value, size));
    }
};

template <typename T>
struct n_dim_vector_generator<0, T> {
    using type = T;
    type operator()(T value, size_t size) {
        return value;
    }
};

用法:

auto test_vector = n_dim_vector_generator<2, long double>{}(2, 3);

演示:https://godbolt.org/z/eiDAUG

为了记录,为了解决评论中的一些问题,C++ 一个惯用的、可初始化的、连续内存类等价于多维 C 数组:嵌套的 std::数组:

For the record, to address some concerns from the comments, C++ has an idiomatic, initializable, contiguous-memory class equivalent of a multi-dimension C array: a nested std::array:

std::array<std::array<long double, COLUMNS>, ROWS> test_array = { /*...*/ };

for (auto& row : test_array)
    for (auto cell : row)
        std::cout << cell << std::endl;

如果你想减少样板以声明一个,你可以使用一个结构:

If you wanted to reduce the boilerplate to declare one, you can use a struct for that:

template <typename T, size_t... N>
struct multi_array;

template <typename T, size_t NFirst, size_t... N>
struct multi_array<T, NFirst, N...> {
    using type = std::array<typename multi_array<T, N...>::type, NFirst>;
};

template <typename T, size_t NLast>
struct multi_array<T, NLast> {
    using type = std::array<T, NLast>;
};

template <typename T, size_t... N>
using multi_array_t = typename multi_array<T, N...>::type;

然后使用:

multi_array_t<long double, ROWS, COLUMNS> test_array = { /*...*/ };

for (auto& row : test_array)
    for (auto cell : row)
        std::cout << cell << std::endl;

这是在堆栈上分配的,就像一个 C 数组.当然,这会占用大量数组的堆栈空间.但是你可以在 std::unique_ptr 周围创建一个装饰器范围,使指向一个的指针更容易访问:

This is allocated on the stack, like a C array. That will eat up your stack space for a big array of course. But you can make a decorator range around std::unique_ptr to make a pointer to one a bit easier to access:

template <typename T, size_t... N>
struct dynamic_multi_array : std::unique_ptr<multi_array_t<T, N...>> {
    using std::unique_ptr<multi_array_t<T, N...>>::unique_ptr;
    constexpr typename multi_array_t<T, N...>::value_type& operator [](size_t index) { return (**this)[index]; }
    constexpr const typename multi_array_t<T, N...>::value_type& operator [](size_t index) const { return (**this)[index]; }
    constexpr typename multi_array_t<T, N...>::iterator begin() { return (**this).begin(); }
    constexpr typename multi_array_t<T, N...>::iterator end() { return (**this).end(); }
    constexpr typename multi_array_t<T, N...>::const_iterator begin() const { return (**this).begin(); }
    constexpr typename multi_array_t<T, N...>::const_iterator end() const { return (**this).end(); }
    constexpr typename multi_array_t<T, N...>::const_iterator cbegin() const { return (**this).cbegin(); }
    constexpr typename multi_array_t<T, N...>::const_iterator cend() const { return (**this).cend(); }
    constexpr typename multi_array_t<T, N...>::size_type size() const { return (**this).size(); }
    constexpr bool empty() const { return (**this).empty(); }
    constexpr typename multi_array_t<T, N...>::value_type* data() { return (**this).data(); }
    constexpr const typename multi_array_t<T, N...>::value_type* data() const { return (**this).data(); }
};

(如果您将这些方法与 nullptr 一起使用,请让买家注意)

(let the buyer beware if you use those methods with nullptr)

然后你仍然可以花括号初始化一个 new 表达式并像容器一样使用它:

Then you can still brace-initialize a new expression and use it like a container:

dynamic_multi_array<long double, ROWS, COLUMNS> test_array {
    new multi_array_t<long double, ROWS, COLUMNS> { /* ... */ }
};

for (auto& row : test_array)
    for (auto cell : row)
        std::cout << cell << std::endl;

演示:https://godbolt.org/z/lUwVE_

这篇关于在 C++ 中生成任意嵌套的向量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆