从初始值设定项列表填充 boost::multi_array 的最佳方法是什么?

问题描述 投票:0回答:2

我想在一些代码中内联初始化 boost::multi_array 。但我不认为 boost::multi_array 支持从初始化列表进行初始化。这是我到目前为止所拥有的:

// First create a primitive array, which can be directly initialized
uint8_t field_primitive[4][8] = {
    { 1,1,1,1,1,1,1,1 },
    { 1,2,1,2,1,2,1,2 },
    { 1,1,2,2,2,2,2,2 },
    { 1,2,2,2,2,2,2,2 }
};
// Create the boost::multi_array I actually want to use
boost::multi_array<uint8_t, 2> field(boost::extents[4][8]);
// Compact but yucky approach to copying the primitive array contents into the multi_array.
memcpy(field.data(), field_primitive, field.num_elements() * sizeof(uint8_t));

我喜欢我可以使用大括号初始化列表来紧凑地表达矩阵内容。但我不喜欢“memcpy”,也不喜欢使用一次性的原始数组。有没有更好的方法来从代码中可读的内联值集填充我的 boost::multi_array ?

c++ c++11 boost boost-multi-array
2个回答
1
投票

官方 boost 文档中有关 multi_array 的以下示例也使用了

memcpy
,但与
origin()
结合使用。所以使用起来似乎还可以:

#include <boost/multi_array.hpp>
#include <algorithm>
#include <iostream>
#include <cstring>

int main()
{
  boost::multi_array<char, 2> a{boost::extents[2][6]};

  typedef boost::multi_array<char, 2>::array_view<1>::type array_view;
  typedef boost::multi_array_types::index_range range;
  array_view view = a[boost::indices[0][range{0, 5}]];

  std::memcpy(view.origin(), "tsooB", 6);
  std::reverse(view.begin(), view.end());

  std::cout << view.origin() << '\n';

  boost::multi_array<char, 2>::reference subarray = a[1];
  std::memcpy(subarray.origin(), "C++", 4);

  std::cout << subarray.origin() << '\n';
} 

关于

origin()
data()
之间的区别,多阵列参考手册定义了以下内容:

元素*数据();这将返回一个指向开头的指针 包含数组数据的连续块。如果所有维度 该数组以 0 为索引并按升序存储,这是 相当于origin()。

元素* origin();这将返回 multi_array 的原始元素。

因此,当将

data()
origin()
memcpy
一起使用时,似乎需要考虑两件事,如果数组包含非 0 索引或不按升序排列的维度:

首先,

origin()
可能不指向数组使用的连续内存块的开头。因此,将多数组大小的内存复制到此位置可能会超出保留的内存块。

其次,另一方面,将内存块复制到

data()
的地址可能会导致内存布局,其中通过多数组访问的数组索引与复制到数组内部数据缓冲区中的内存块的索引不对应。

所以在我看来,使用

memcpy
来(预)填充多数组应该谨慎使用,最好使用基于 0 的索引和升序。


0
投票

首先,没有理由使用

memcpy
。 您看到的示例代码使用
memcpy
只是因为它将 C 字符串转换为
char
数组。
std::copy[_n]
在这里更好:

...
boost::multi_array<uint8_t, 2> field(boost::extents[4][8]);
std::copy_n(&field_primitive[0][0], field.num_elements(), field.data());

其次,您可以编写一个辅助函数,它接受一个 c 数组并返回您想要的数组:

template<std::size_t N, std::size_t M, class T>
boost::multi_array<T, 2> make_boost_multi_array(T const(&in)[N][M]) {
    boost::multi_array<uint8_t, 2> ret(boost::extents[N][M]);
    std::copy_n(&in[0][0], ret.num_elements(), ret.data());
    return ret;
}
...
boost::multi_array<uint8_t, 2> field = make_boost_multi_array(field_primitive);

看到冗余较少。

事实上,你不再需要辅助 c 数组了:

boost::multi_array<uint8_t, 2> field = make_boost_multi_array<4, 8, uint8_t>({
  { 1,1,1,1,1,1,1,1 },
  { 1,2,1,2,1,2,1,2 },
  { 1,1,2,2,2,2,2,2 },
  { 1,2,2,2,2,2,2,2 }
});

boost::multi_array<uint8_t, 2> field = make_boost_multi_array(
  (uint8_t[4][8]){
    { 1,1,1,1,1,1,1,1 },
    { 1,2,1,2,1,2,1,2 },
    { 1,1,2,2,2,2,2,2 },
    { 1,2,2,2,2,2,2,2 }
  }
);

最后,您可以使用类似的多维数组库(不是Boost.MultiArray),它本身支持初始化列表(免责声明,我编写了该库):

#include<multi/array.hpp>  // library from https://gitlab.com/correaa/boost-multi/
...
multi::array<uint8_t, 2> field = {
    { 1,1,1,1,1,1,1,1 },
    { 1,2,1,2,1,2,1,2 },
    { 1,1,2,2,2,2,2,2 },
    { 1,2,2,2,2,2,2,2 }
};

根本没有冗余。

请参阅此处编译的所有示例:https://godbolt.org/z/YEdsTMGKz

© www.soinside.com 2019 - 2024. All rights reserved.