【问题标题】:std::copy for multidimensional arrays多维数组的 std::copy
【发布时间】:2015-01-12 21:55:13
【问题描述】:

前几天我试过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]);
}

}

【问题讨论】:

  • 这是一个遗漏(但我猜,很大程度上是故意的)。通过不使用数组的数组来解决它。
  • 使用std::array 代替普通数组可以解决问题。
  • @Zyx2000 为什么一直忽略多维数组?是的,std::array 也有帮助,但数组类型是 C++ 的一部分,就像 std::array

标签: c++ arrays c++11 multidimensional-array


【解决方案1】:

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

例如你可以写

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

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

【讨论】:

  • 是的,但是reinterpret_cast是丑的化身,你不同意吗?
  • @user1095108 复制多维数组最简单的方法。
  • 如果可以的话,解释一下可能有什么替代方案,强制转换解决方案是通用的(如果您使用sizeof 运算符会更好),但for_each 解决方案不是。
  • memcpy(&amp;a, &amp;b, sizeof(a));
  • @jrok 如果元素不能轻易复制怎么办?
【解决方案2】:

它不适用于简单的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) );

Demo.

【讨论】:

  • 哇,实际上我对 AUTO_RETURN 技巧比对您的解决方案更感兴趣。你从哪里来的?
  • __VA_ARGS__ 不是仍然不标准吗?
  • @user1095108 不,它是自 C++11 以来的标准。 [cpp.subst]/2:“出现在替换列表中的标识符 __VA_ARGS__ 应被视为参数,可变参数应形成用于替换它的预处理标记。”
  • @user1095108 noexcept 是一个标准化的关键字,是的。尽管没有声明 std::begin 重载 noexcept,但这里没有意义。
  • 没错,但是您的decayed_* 函数不是标准的。啊,我明白了,是因为转发,好吧。
【解决方案3】:

阅读完所有答案后,这是我的 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]);
}

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

【讨论】:

  • 这个答案很有趣。我对其进行了测试,但观察到它只复制了几个元素而不是整个表格。这是因为sizeof(array)/sizeof(T) 只应用于数组的外部维度(10 个元素)。
  • @Christophe 现在可以用了吗?我真的很想避免所有那些丑陋的演员表。
【解决方案4】:

这个答案已经找到了一些非常有趣的答案。大多数基于 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>
    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&lt;T&gt;::type 删除维度并获取基本类型 (int)。

    【讨论】:

      【解决方案6】:

      如何使用带有扁平化版本的联合。

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

      【讨论】:

      • 不错的技巧。但并不总是有用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-12
      • 2022-01-24
      • 1970-01-01
      • 2020-12-24
      • 2011-09-21
      • 1970-01-01
      相关资源
      最近更新 更多