简体   繁体   English

用于多维数组的std :: copy

[英]std::copy for multidimensional arrays

I've tried the other day with gcc-4.9.1 : 我前几天用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;
}

and, of course, it produced an error: 当然,它产生了一个错误:

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,
    ^

I think this is not so much a problem with std::copy() , as with std::begin() and std::end() that would handle multidimensional arrays. 我认为这不是std::copy() ,就像处理多维数组的std::begin()std::end() Is this an omission with the current C++ standard and how to work around it? 这是对当前C ++标准的遗漏以及如何解决它吗?

EDIT: I am convinced, the standard could fix this problem: 编辑:我相信,该标准可以解决这个问题:

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]);
}

}

Arrays have no assignment operator. 数组没有赋值运算符。 So you need to copy the whole array element by element of type int. 所以你需要按int类型的元素复制整个数组元素。

For example you can write 例如,你可以写

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

Or you can use std::for_each or std::transform with some compound lambda expressions. 或者你可以使用std::for_eachstd::transform和一些复合lambda表达式。

It won't work with a simple copy call as plain arrays cannot be copied or assigned. 它不适用于简单的copy调用,因为无法复制或分配普通数组。 However, you can copy the underlying elements and treat the multidimensional array as a one-dimensional one. 但是,您可以复制基础元素并将多维数组视为一维数组。

One convenient possibility is to use a flattened range accessors: 一个方便的可能性是使用扁平范围访问器:

// 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))

Then, 然后,

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

Demo . 演示

Here another template variant, which works for any multimensional array , whatever the number of dimensions. 这里是另一个模板变体,适用于任何多维数组 ,无论维数多少。 It uses the fact that elements of a multidimensional are contiguous and casting to process the array as if it was a flat one dimensional one: 它使用了这样一个事实:多维元素是连续的并且用于处理数组,就好像它是一个平面的一维:

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);
}

You call it: 你叫它:

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

The trick here is to use the type T for the multidimensional array ( int[][]..[] ) and use typename std::remove_all_extents<T>::type to remove the dimensions and get the base type ( int ). 这里的技巧是使用类型T作为多维数组( int[][]..[] )并使用typename std::remove_all_extents<T>::type来删除维度并获取基本类型( int )。

After reading through all the answers, here's my 5 cents, that seem to work: 阅读完所有答案后,这是我的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]);
}

I won't accept my own answer and, if something is wrong, downvote or comment and I'll remove. 我不会接受我自己的答案,如果出现问题,请进行投票或评论,我将删除。

This answer has already found some very interesting answers. 这个答案已经找到了一些非常有趣的答案。 Most are based on the fact that the C++ standard ensures that elements of a multidimensional array are contiguous , making it possible (via casting) to process the array as if it was a one dimensional one. 大多数都基于以下事实:C ++标准确保多维数组的元素是连续的 ,从而可以(通过强制转换)处理数组,就好像它是一维数组一样。

I propose this variant for the sake of completeness. 为了完整起见,我提出了这个变种。 It uses templates and automatic deduction of array size: 它使用模板和自动扣除数组大小:

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; 

You can then proceed with the copy: 然后,您可以继续复制:

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

Another approach could be to define a 3D iterator using the same template approach. 另一种方法可以是使用相同的模板方法定义3D 迭代器

How about using a union with a flattened version. 如何使用带有扁平版本的联合。

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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM