std::copy用于多维数组

std::copy for multidimensional arrays

本文关键字:数组 用于 copy std      更新时间:2023-10-16

我前几天用gcc-4.9.1试过:

int main()
{
  int a[10][20][30];
  int b[10][20][30];
  ::std::copy(::std::begin(a), ::std::end(a), ::std::begin(b));
  return 0;
}

当然,它产生了一个错误:

In file included from /usr/include/c++/4.9.2/bits/char_traits.h:39:0,
                 from /usr/include/c++/4.9.2/ios:40,
                 from /usr/include/c++/4.9.2/ostream:38,
                 from /usr/include/c++/4.9.2/iostream:39,
                 from t.cpp:1:
/usr/include/c++/4.9.2/bits/stl_algobase.h: In instantiation of 'static _Tp* std::__copy_move<_IsMove, true, std::random_access_iterator_tag>::__copy_m(const _Tp*, const _Tp*, _Tp*) [with _Tp = int [20][30]; bool _IsMove = false]':
/usr/include/c++/4.9.2/bits/stl_algobase.h:396:70:   required from '_OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = false; _II = int (*)[20][30]; _OI = int (*)[20][30]]'
/usr/include/c++/4.9.2/bits/stl_algobase.h:434:38:   required from '_OI std::__copy_move_a2(_II, _II, _OI) [with bool _IsMove = false; _II = int (*)[20][30]; _OI = int (*)[20][30]]'
/usr/include/c++/4.9.2/bits/stl_algobase.h:466:17:   required from '_OI std::copy(_II, _II, _OI) [with _II = int (*)[20][30]; _OI = int (*)[20][30]]'
t.cpp:10:62:   required from here
/usr/include/c++/4.9.2/bits/stl_algobase.h:373:4: error: static assertion failed: type is not assignable
    static_assert( is_copy_assignable<_Tp>::value,
    ^

我认为这不是std::copy()的问题,而是处理多维数组的std::begin()std::end()的问题。这是当前C++标准的遗漏以及如何解决它?

编辑:我相信,该标准可以解决此问题:

namespace std
{
template <typename T, size_t M, size_t N>
constexpr typename remove_all_extents<T>::type*
begin(T (&array)[M][N])
{
  return begin(array[0]);
}
template <typename T, size_t M, size_t N>
constexpr typename remove_all_extents<T>::type*
end(T (&array)[M][N])
{
  return end(array[M - 1]);
}
}

它不适用于简单的copy调用,因为无法复制或分配纯数组。但是,您可以复制基础元素并将多维数组视为一维数组。

一种方便的可能性是使用扁平化的范围访问器:

// For convenient trailing-return-types in C++11:
#define AUTO_RETURN(...) 
 noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) {return (__VA_ARGS__);}
template <typename T>
constexpr auto decayed_begin(T&& c)
AUTO_RETURN(std::begin(std::forward<T>(c)))
template <typename T>
constexpr auto decayed_end(T&& c)
AUTO_RETURN(std::end(std::forward<T>(c)))
template <typename T, std::size_t N>
constexpr auto decayed_begin(T(&c)[N])
AUTO_RETURN(reinterpret_cast<typename std::remove_all_extents<T>::type*>(c    ))
template <typename T, std::size_t N>
constexpr auto decayed_end(T(&c)[N])
AUTO_RETURN(reinterpret_cast<typename std::remove_all_extents<T>::type*>(c + N))

然后

std::copy( decayed_begin(a), decayed_end(a), decayed_begin(b) );

演示

数组没有赋值运算符。因此,您需要按 int 类型的元素复制整个数组元素。

例如,你可以写

std::copy( reinterpret_cast<int *>( a ),
           reinterpret_cast<int *>( a ) + 10 * 20 * 30,
           reinterpret_cast<int *>( b ) );

或者,您可以将 std::for_eachstd::transform 与一些复合 lambda 表达式一起使用。

这是另一个模板变体,它适用于任何多尺寸数组,无论维数如何。 它利用多维元素是连续的和强制转换的事实来处理数组,就好像它是一个平面的一维数组一样:

template <typename T>
typename std::remove_all_extents<T>::type* mbegin(T& arr) {
    return reinterpret_cast<typename std::remove_all_extents<T>::type*>(&arr);
}
template <typename T>
size_t msize(const T& a) 
{
    return sizeof(T) / sizeof(typename std::remove_all_extents<T>::type);
}
template <typename T>
typename std::remove_all_extents<T>::type* mend(T& arr) {
    return reinterpret_cast<typename std::remove_all_extents<T>::type*>(&arr)+msize(arr);
}

你称之为:

::std::copy(mbegin(a), mend(a), mbegin(b));

这里的诀窍是使用类型 T 作为多维数组 (int[][]..[] ),并使用 typename std::remove_all_extents<T>::type 删除维度并得到基类型 (int)。

这个答案已经找到了一些非常有趣的答案。 大多数是基于这样一个事实,即C++标准确保多维数组的元素是连续的,从而可以(通过强制转换)像处理一维数组一样处理数组。

为了完整起见,我提出这个变体。 它使用模板和自动扣除数组大小:

template <typename T, size_t N1, size_t N2, size_t N3>
T* begin(T(&arr)[N1][N2][N3]) {
    return reinterpret_cast<T*>(arr);  
}
template <typename T, size_t N1, size_t N2, size_t N3>
T* end (T(&arr)[N1][N2][N3]) {
    return reinterpret_cast<T*>(arr)+N1*N2*N3; 

然后,您可以继续复制:

::std::copy(begin(a), end(a), begin(b));

另一种方法是使用相同的模板方法定义 3D 迭代器

在阅读了所有答案之后,这是我的 5 美分,似乎有效:

template <typename T>
constexpr T* begin(T& value) noexcept
{
  return &value;
}
template <typename T, ::std::size_t N>
constexpr typename ::std::remove_all_extents<T>::type*
begin(T (&array)[N]) noexcept
{
  return begin(array[0]);
}
template <typename T>
constexpr T* end(T& value) noexcept
{
  return &value + 1;
}
template <typename T, ::std::size_t N>
constexpr typename ::std::remove_all_extents<T>::type*
end(T (&array)[N]) noexcept
{
  return end(array[N - 1]);
}

我不会接受我自己的答案,如果有什么问题,投反对票或评论,我会删除。

使用带有

扁平版本的联合怎么样?

int main()
{
  union
  {
    int a[10][20][30];
    int fa[10 * 20 * 30];
  };
  union
  {
    int b[10][20][30];
    int fb[10 * 20 * 30];
  };
  ::std::copy(::std::begin(fa), ::std::end(fa), ::std::begin(fb));
  return 0;
}