C++通过引用传递数组

98

能否通过引用传递数组?

 void foo(double& *bar) 

看起来我的编译器不支持。为什么?传递数组的正确方式是什么?或者有没有解决方法?我有一个应该被修改并且之后要检索的数组参数,这是我的方法的输入,另外,我可以将此数组作为类成员,这样也可以工作,但对于代码的其他部分有很多缺点(我想避免)。

谢谢和问候。


39
请查看顺时针螺旋法则 - Some programmer dude
1
你考虑过使用 std::vector 或类似的容器吗? - moooeeeep
2
如果大小是动态/未知的,那么std::vector显然更优越,但在大小始终相同的情况下,使用std::array会更好,因为它可以解决语法上的丑陋问题,同时也赋予了值语义。 - underscore_d
请查看 https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper。 - sdevikar
为什么不直接使用 void foo(double bar[ ])?如果您愿意,也可以提供数组的长度,如 void foo(double bar[2])。除非需要避免复制构造函数,否则对于复杂类型来说这是可行的。 - ultimate cause
@ultimatecause 这并没有声明/传递一个数组参数,只是一个指向第一个元素的指针。包括大小也没有任何区别;它会被丢弃。即使它按照你的想法工作,你也不会通过引用而是通过值传递,这是浪费和无意义的。 - underscore_d
7个回答

167

实际上,只能通过引用传递数组:

void foo(double (&bar)[10])
{
}

这可以防止你做一些像以下这样的事情:

double arr[20];
foo(arr); // won't compile

为了能够将任意大小的数组传递给foo,请将其制作成模板,并在编译时捕获数组的大小:
template<typename T, size_t N>
void foo(T (&bar)[N])
{
    // use N here
}

你应该认真考虑使用 std::vector,或者如果你有支持c++11的编译器,可以使用std::array


7
然而,一旦传递到 foo 函数中,数组会衰变成指针。不,它不会,因为你只能传递一个有10个元素的数组,你不需要更多关于大小的信息。再次强调,你不能将数组作为参数传递到 void foo( double*& ) 函数中;隐式转换的结果是一个右值,不能用来初始化非 const 引用。你必须使用 void foo( double *const & ); - James Kanze
1
太好了 :) 这正是我要找的。在我的模板中,我使用了 void foo(T &bar[N]) - 但实际上这意味着“引用数组”,而不是“数组引用”。 - OLL
如果这样的函数已经存在,而你只有一个正确大小的std::vector,你该如何将向量的内容(可能通过std::vector::data()获得)转换为此数组类型?在类型参数中使用固定数组大小的语法是什么(即用于static_cast<>)? - davidA
1
@meowsqueak,你不会这样做的,因为强制类型转换会导致未定义的行为,据我所知;我没有看到reinterpret_cast中允许将动态分配的数组(在分配后没有固有大小)视为具有固定大小的自动数组。此外,这是错误的问题:正确的问题应该是“如何重构我的代码以适用于两种类型的容器?”,答案是模板和迭代器/范围,或者由于您说大小是固定的,一个只接受数据指针/引用并硬编码大小的函数(如果大小可能变化,则使用数据/大小适配器,例如span)。 - underscore_d
为什么不直接使用void foo(double bar[ ])? 如果您愿意,也可以提供数组的长度,如void foo(double bar[2])。除非需要避免复制构造函数,否则对于复杂类型来说这是必要的。 - ultimate cause

20

可以,但是当引用进行参数匹配时,隐式将数组转换为指针是不自动进行的,因此您需要像这样使用:

void foo( double (&array)[42] );
或者
void foo( double (&array)[] );

需要注意的是,在匹配类型时,double [42]double [] 是不同的类型。如果你有一个未知维度的数组,它将与第二个匹配,但不会与第一个匹配;如果你有一个有42个元素的数组,它将与第一个匹配,但不会与第二个匹配(后者在我看来非常违反直觉)。

在第二种情况中,你还必须传递维度,因为一旦进入函数,就没有办法恢复它。


8
第二个对我来说无法编译。 - jrok
对我来说也一样。我不知道是由于编译器错误还是因为我忽略了标准中的某些内容。我实际上从未想过要这样做。(如果维度是模板参数,则第一个选项非常有用,因为编译器会为您计算出正确的维度。) - James Kanze
6
我认为第二个例子不符合C++标准要求。顺便提一下,它无法在GCC 4.8或Clang 3.4编译通过。我认为需要一个带模板的大小。 - Ricky65
3
@Ricky65 是的,确实如此。我不知道为什么。对于不完整类型的引用通常允许作为函数参数。(当然,在这种情况下,当类型完成时会发生变化:double []double [42]是两种不同的类型。因此,你只能传递一个长度未知的数组,例如extern double d[];或类似的方式。这严重限制了它的可用性。) - James Kanze
2
也许应该修正这个答案以反映这一点? - CoffeeTableEspresso

8

5
是的,std::vector 通常是最好的解决方案。但也有例外情况。 - James Kanze
1
从C++ 20开始,std::span会更可取,如果不需要数组的增长,它比std::vector更灵活。 - undefined

6

如果您只想修改元素:

void foo(double *bar);

足够了。

如果您想修改地址以使用(例如:realloc),但对于数组不起作用:

void foo(double *&bar);

这是正确的表单。


5
除了您无法将数组传递给后者之外,他问的是关于数组而不是指针的。 - James Kanze
@JamesKanze: 对的。第二种情况在堆栈上的数组上不起作用,在这种情况下,他应该使用第一种形式。 - Naszta
8
第二种情况对于数组是行不通的,只有当你有实际的指针类型变量时才能起作用。 - James Kanze

4

这里,Erik解释了所有以引用方式传递数组的方法:https://dev59.com/02025IYBdhLWcg3w6aUX#5724184

同样地,你可以像这样创建一个数组引用变量

int arr1[] = {1, 2, 3, 4, 5};
int(&arr2)[5] = arr1;

2
就像其他答案所说的一样,在*后面加上&
这带出了一个有趣的点,有时可能会令人困惑:类型应该从右向左阅读。例如,这是(从最右边的*开始)指向int的常量指针。
int * const *x;

你所写的内容因此将成为指向引用的指针,这是不可能的。

改变顺序仍然无法让他传递一个数组。它需要一个实际的指针对象,而不是转换的结果。 - James Kanze

2

8.3.5.8 如果参数的类型包括形式为“指向未知界限的T数组的指针”或“未知边界的T数组的引用”的类型,则程序不良形式


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