且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

从std :: array获取对原始数组的引用

更新时间:2023-02-24 09:51:51

获取std :: array的基础raw(C)的规范方法是什么? 数组?

无法获取基础的C数组.

此外,是否有充分的理由为什么data()返回原始指针,而不是 对基础原始数组的引用,还是仅仅是一个疏忽?

这是倒退的:std::array没有充分的理由提供底层的C数组.正如您已经说过的那样,C数组(仅在原始指针上)才有用,仅当函数获得对C数组的引用时.

您上一次拥有函数的时间是

void foo(int (&arr)[5])

我吗?绝不.除了获取数组的大小(并拒绝指针)外,我从未见过带有C数组引用参数的函数:

template <class T, std::size_t N>
auto safe_array_size(T (&)[N]) { return N; }


让我们深入探讨为什么不使用对数组的参数引用.

对于初学者来说,由于数组到指针的衰减和缺少引用类型,从C区域指针中使用单独的size参数是传递数组的唯一方法.

在C ++中,有C数组的替代方法,例如std::vectorstd::array.但是,即使有一个(旧)C数组,也有两种情况:

  • 如果将其传递给C函数,则没有引用选项,因此您只能使用指针+大小
  • 当您要将其传递给C ++函数时,惯用的C ++方法是传递开始+结束指针.

首先,begin + end迭代器是通用的,它接受任何类型的容器.但是当您想避免使用模板时,经常看到对std::vector的引用,那么如果有一个数组,为什么不引用C数组呢?因为有一个很大的缺点:您必须知道数组的大小:

void foo(int (&arr)[5])

这是非常有限的.

要解决此问题,您需要使其成为模板:

template <std::size N>
void foo(int (&arr)[N])

达到了避免模板的目的,因此***使用begin + end模板迭代器.


在某些情况下(例如,仅对具有2或3个值的数学计算 相同的语义,因此它们不应是单独的参数) 需要特定的数组大小,并使该函数通用 没道理.在这种情况下,请指定数组的大小 保证安全,因为它只允许传递一组 编译时大小正确;因此,这是有利的,而不是 大弊端"

(C和)C ++的优点之一是适用范围广.因此,是的,您总是会发现一些以独特的方式使用或需要某些独特功能的字段.话虽如此,即使在您的示例中,我仍然会回避数组.当您有一定数量的不应该在语义上分开的值时,我认为在大多数情况下,结构是对数组的正确选择(例如glm::mat4而不是float[4]).

但我们不要忘记std::array是什么:C数组的现代替代品.我在分析期权时了解到的一件事是,没有绝对的优于".总有一个依赖".但不是在这种情况下:std::array无疑应该替换接口中的C数组.因此,在极少数情况下需要固定大小的容器作为参考参数的情况下,当您已有std::array时,鼓励使用C数组是没有意义的.因此,唯一需要公开std::array的基础C数组的有效情况是某些具有C数组引用参数的旧库.但是我认为从更大的角度来看,将其添加到界面中是没有道理的.新代码应使用结构(btw std::tuple越来越容易被每个标准使用)或std::array.

What's the canonical way to get the reference to std::array's underlying raw (C) array?

The data() method returns just a raw pointer, which makes it unsuitable e.g. for passing into functions which accept a reference to a raw array of a known size.

Also, is there a good reason why data() returns a raw pointer, and not a reference to the underlying raw array, or is this just an oversight?

What's the canonical way to get an std::array's underlying raw (C) array?

There is no way of getting the underlying C array.

Also, is there a good reason why data() returns a raw pointer, and not a reference to the underlying raw array, or is this just an oversight?

It's backwards: there is no good reason for the std::array to provide the underlying C array. As you already said, the C array would be useful (over the raw pointer) only with functions getting a reference to C arrays.

When was the last time you had a function:

void foo(int (&arr)[5])

Me? Never. I never saw a function with a C array reference parameter with the exception of getting the size of array (and rejecting pointers):

template <class T, std::size_t N>
auto safe_array_size(T (&)[N]) { return N; }


Let's dive a little into why parameters references to arrays are not used.

For starters, from the C area pointer with a separate size parameter was the only way to pass arrays around, due to array-to-pointer decay and lack of reference type.

In C++ there are alternatives to C arrays, like std::vector and std::array. But even when you have a (legacy) C array you have 2 situations:

  • if you pass it to a C function you don't have the option of reference, so you are stuck to pointer + size
  • when you want to pass it to a C++ function the idiomatic C++ way is to pass begin + end pointers.

First of all a begin + end iterators is generic, it accepts any kind of containers. But is not uncommon to see reference to std::vector when you want to avoid templates, so why not reference to C array if you have one? Because of a big drawback: you have to know the size of the array:

void foo(int (&arr)[5])

which is extremely limiting.

To get around this you need to make it a template:

template <std::size N>
void foo(int (&arr)[N])

which beats the purpose of avoiding templates, so you better go with begin + end template iterators instead.


In some cases (e.g. math calculations on just 2 or 3 values which have the same semantics, so they shouldn't be separate parameters) a specific array size is called for, and making the function generic wouldn't make sense. In those cases, specifying the size of the array guarantees safety since it only allows passing in an array of the correct size at compile-time; therefore it's advantageous and isn't a "big drawback"

One of the beauties of (C and) C++ is the enormous scope of applicability. So yes, you will always find some fields that use or need a certain unique feature in an unique way. That being said, even in your example I would still shy away from arrays. When you have a fixed number of values that shouldn't be semantically separated I think a structure would be the correct choice over arrays most of the time (e.g. glm::mat4 instead of float[4]).

But let's not forget what std::array is: a modern replacement for C arrays. One thing I learned when analyzing options is that there is no absolute "better than". There is always a "depends". But not in this case: std::array should unquestionably replace C arrays in interfaces. So in the rare case where a fixed size container is needed as a reference parameter it doesn't make sense to enable encouraging the use of C arrays when you already have an std::array. So the only valid case where exposing the underlying C array of std::array is need is for some old libraries that have C array reference parameters. But I think that in the bigger picture adding this to the interface it is not justified. New code should use a struct (btw std::tuple is getting easier and easier to use by each standard) or std::array.