【问题标题】:Generically obtaining a pointer to a multi-dimensional C++ array's first element一般获取指向多维 C++ 数组第一个元素的指针
【发布时间】:2014-06-21 02:45:06
【问题描述】:

如果我想设置一个指向多维 C++ 数组的第一个元素的指针,我可以轻松做到:

double arr[2][3][4];
double *p;
p = &arr[0][0][0];

如何对任意等级的数组执行此操作?接收对多维数组的引用的函数可以写成如下:

template <typename T, unsigned N>
void receive(T (&a)[N]) {}

C++11 std::remove_all_extents 类模板可用于获取数组的(最深的)元素类型。这可以帮助使用typename std::remove_all_extents&lt;T&gt;::type * 构造receive 的修改版本的返回类型。但是返回的value应该如何创建呢?

以下解决方案使用reinterpret_cast。这大概会高效执行,但我不喜欢reinterpret_cast 对类型系统的规避。

template <typename T, unsigned N>
typename std::remove_all_extents<T>::type *
to_ptr1(T (&a)[N])  { return reinterpret_cast<
                               typename std::remove_all_extents<T>::type *
                             >(&a); }

或者,以下解决方案使用递归,虽然类型系统仍然强大,但递归可能会引入额外的指令。

template <typename T> T *to_ptr2(T &a)  { return &a; }

template <typename T, unsigned N>
typename std::remove_all_extents<T[N]>::type *
to_ptr2(T(&a)[N])  { return to_ptr2(*(&a[0])); }

一般如何获取多维数组的第一个元素的地址?有零开销的强类型解决方案吗?

【问题讨论】:

  • reinterpret_cast 解决方案中没有强类型化的内容是什么? IMO 很好,我认为没有比这更好的了。
  • 我没有练习元编程魔法,但我很确定解决方案将涉及std::rank
  • reinterpret_cast 版本没有问题 - 数组保证没有初始填充。我想知道remove_all_extents 是否无论如何都不是通过递归模板实现的。
  • remove_all_extents 将使用递归实现。这里计算的结果是一个类型 - 这必须在编译时完成。

标签: c++ arrays templates types multidimensional-array


【解决方案1】:
#include <iostream>
#include <type_traits>    

template<size_t Rank>
struct deref_n
{
    template<typename T>
    auto operator()(T& ptr) -> decltype(deref_n<Rank-1>()(*ptr))
    {
        return deref_n<Rank-1>()(*ptr);
    }
};

template<>
struct deref_n<0>
{
    template<typename T>
    T& operator()(T& val)
    {
        return val;
    }
};

template<typename T>
auto first_element(T& val) -> decltype(deref_n<std::rank<T>::value>()(val))
{
    return deref_n<std::rank<T>::value>()(val);
}


int main()
{
    int x[10][20][30] = {};
    first_element(x) = 777;             // this gives me a reference to x[0][0][0]
    std::cout << x[0][0][0] << '\n';
}

【讨论】:

  • 谢谢本杰明。唉,这似乎遵循与原始问题中的to_ptr2 非常相似的递归方案。
  • 模板递归可能会在编译时解决...如果这与您有关,请尝试检查编译器生成的程序集
  • 嗨,马特。我只关心最好的解决方案。如果递归策略总是意味着我应该检查程序集,这将不利于它。
  • 您不需要检查程序集。模板实例化总是在编译时解析。
  • 模板实例化总是在编译时解析;然而,对实例化对象的函数调用可以在运行时执行。例如,使用带有 3D 数组的 deref_nto_ptr2,将可能在运行时导致 3 次函数调用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-17
  • 1970-01-01
相关资源
最近更新 更多