在 C++ 中生成任意嵌套的向量

Posted

技术标签:

【中文标题】在 C++ 中生成任意嵌套的向量【英文标题】:Generate Arbitrarily Nested Vectors in C++ 【发布时间】:2020-01-29 15:44:43 【问题描述】:

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

    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 函数更复杂的用法:

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

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

    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函数的实现细节如下。

#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&lt;std::vector&lt;long double,std::allocator&lt;long double&gt;&gt;,std::allocator&lt;std::vector&lt;long double,std::allocator&lt;long double&gt;&gt;&gt;&gt;' 我知道它是由作为递归结构终止条件的if (N == 0) 块引起的。但是,如果我尝试将终止条件编辑为单独的形式。

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': illegal use of explicit template arguments。有没有更好的办法解决这个问题?

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

【问题讨论】:

我的建议是不要这样做。多维向量缺乏数据局部性,可能成为性能瓶颈。如果您将一维向量包装在一个可以使用数学伪造它具有多个维度的类中,它会更有效且更容易实现。类似这样的东西:***.com/a/43358434/4342498 为了简化@NathanOliver 所说的内容,您可能已经知道 arr2D[i][j] 与 arr1D[i * k + j] 类似,只需修改控制循环即可。这个基本思想可以扩展到 N 维。 感谢@NathanOliver 的及时回复。由于我正在从事的项目的某些原因。我想设计这种功能用于测试目的。 感谢@Ardent 的及时回复和详细描述。考虑到std::vector的特点和使用方便,我认为std::vector在内存管理上比arr2D更安全,功能更完善。我知道使用智能指针可以实现内存安全的arr2D,但是这种方式很难处理多维分配。 澄清一下,您是专门寻找这个奇怪的一元多维向量,还是寻找通用的多维数据结构? 【参考方案1】:

实现你的具体映射:

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

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

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

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

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


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

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

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

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

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 周围创建一个装饰器范围,以使指向其中的指针更易于访问:

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 一起使用,请让买家小心)

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

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++ 中生成任意嵌套的向量的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中生成随机双精度数

在matlab中生成具有一般维数的网格

如何在 C++ 中生成 UUID,而不使用 boost 库?

是否可以在单个 Eloquent 查询中生成 3 个嵌套层?

R从n个元素的字符向量中生成大小为m的所有可能组合[重复]

如何在 C++ 中生成 mhtml 文件? [关闭]