c++ 从初始化列表填充boost::multi_array的最佳方式是什么?

cwtwac6a  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(133)

我想在一些代码中内联地初始化一个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?

ozxc1zmp

ozxc1zmp1#

下面的例子来自官方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()之间的区别,多阵列参考手册定义如下:
return();这将返回一个指向包含数组数据的连续块的开头的指针。如果数组的所有维度都是0索引并按升序存储,则这等效于origin()。
element * origin();返回multi_array的原始元素。
因此,在使用data()origin()以及memcpy时,似乎需要考虑两件事,ff数组包含非0索引或非升序的维度:
首先,origin()可能不指向数组所使用的连续内存块的开始。因此,将多数组大小的内存复制到此位置可能会超过保留的内存块。
其次,另一方面,将内存块复制到data()的地址可能会导致内存布局,其中通过multiarray访问的数组索引与复制到数组内部数据缓冲区的内存块的索引不对应。
所以在我看来,使用memcpy来(预)填充多数组应该小心使用,最好是使用基于0的索引和升序。

jfgube3f

jfgube3f2#

首先,没有理由使用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());

字符串
其次,你可以写一个helper函数,它接受一个c-array并返回你想要的数组:

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

相关问题