获取多维C++数组第一个元素的指针的通用方法

5

如果我想将一个指针设置为多维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<T>::type *构造receive的修改版本的返回类型。但是,该如何创建返回的值呢?
以下解决方案使用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的解决方案中,什么不是强类型?我认为这很好,而且我认为它不会有任何改进。 - jrok
我已经有一段时间没有使用元编程技巧了,但我相信解决方案一定涉及到 std::rank - Benjamin Lindley
reinterpret_cast 版本没有问题 - 数组保证不具有初始填充。我想知道 remove_all_extents 是否已经通过递归模板实现。 - M.M
remove_all_extents 将使用递归实现。在这里,计算的结果是一种类型 - 这必须在编译时完成。 - user2023370
1个回答

0
#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';
}

谢谢Benjamin。不幸的是,这似乎遵循与原问题中的to_ptr2非常相似的递归方案。 - user2023370
模板递归可能在编译时解决...如果这让您担心,请尝试检查您的编译器生成的汇编。 - M.M
嗨,马特。我只关心最佳解决方案。如果递归策略总是意味着我应该检查汇编代码,那么这将对它产生不利影响。 - user2023370
你不需要检查汇编代码。模板实例化总是在编译时解析的。 - tclamb
模板实例化总是在编译时解析的;然而,对已实例化对象的函数调用可能在运行时执行。例如,对一个3D数组使用'deref_n'或'to_ptr2',将可能导致在运行时进行3个函数调用。 - user2023370

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接