C ++通过引用传递数组


78

允许通过引用传递数组吗?

 void foo(double& *bar) 

似乎我的编译器说不。为什么?通过引用传递数组的正确方法是什么?还是可以解决?我有一个数组参数,我的方法应修改该参数,然后再检索。另外,我可以使该数组成为一个类成员,可以很好地工作,但是它对我代码的其他部分有很多缺点(我想避免)。

感谢致敬。



1
您是否考虑过使用std::vector
moooeeeep 2012年

1
std::vector如果大小是动态的/未知的,@ moooeeeep显然是更好的选择,但是在大小总是相同的情况下,它会显得过大,从而std::array解决了语法上的丑陋,同时还赋予了值语义。
underscore_d


为什么不仅仅使foo(double bar [])无效?如果愿意,还可以将数组的长度提供为void foo(double bar [2])。除非是复杂类型,否则需要避免复制构造函数。
最终原因是

Answers:


131

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

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 & );
詹姆斯甘孜

1
很棒:)这正是我想要的。我void foo(T &bar[N])在模板中进行操作-但这实际上是“引用数组”而不是“引用数组”。
OLL,2015年

如果已经存在这样的函数,而您所拥有的只是一个std::vector(大小正确),您将如何将向量的内容(可能是通过获取的std::vector::data())转换为该数组类型?类型参数中固定数组大小的语法是什么(即与一起使用static_cast<>)?
davidA

1
@meowsqueak您不会,因为演员表将具有不确定的行为,AFAICT;我看不到reinterpret_cast将动态分配的数组(分配后没有内在大小)视为具有固定大小的自动数组时没有任何余地。此外,这是一个错误的问题:正确的问题是“如何重构代码以同时使用两种类型的容器?”,答案是模板和迭代器/范围,或者因为您说的大小是固定的,所以一个函数仅使用数据指针/ ref并硬编码大小(或者,如果大小可能不同,则使用数据/大小适配器,如span
underscore_d

为什么不仅仅使foo(double bar [])无效?如果愿意,还可以将数组的长度提供为void foo(double bar [2])。除非是复杂类型,否则需要避免复制构造函数。
最终原因是

18

是的,但是当对引用进行参数匹配时,指向指针的隐式数组不是自动的,因此您需要执行以下操作:

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

要么

void foo( double (&array)[] );

但是请注意,匹配时,double [42]double []是不同的类型。如果您有一个未知维度的数组,它将与第二个匹配,但不匹配第一个;如果您有一个包含42个元素的数组,则将与第一个匹配但不与第二个匹配。(恕我直言,后者非常违反直觉。)

在第二种情况下,您还必须传递维度,因为一旦进入函数内部就无法恢复它。


7
第二个不适合我编译。
jrok '04

对我来说都不是。我不知道这是由于编译器错误还是我忽略的标准中的某些原因。我从来没有真正想要这样做。(如果维度是模板参数,则第一个非常有用,因为编译器会为您找到正确的维度。)
James Kanze 2012年

6
我不认为第二个示例是符合标准的有效C ++。顺便说一句,它不能与GCC 4.8或Clang 3.4一起编译。我认为模板大小是必需的。
Ricky65

2
@ Ricky65就是这样。我想知道为什么。通常允许引用不完整类型作为函数参数。(当然,在这种情况下,类型会在完成时发生变化:double []并且double [42]是两种不同的类型。因此,您可以传递给后一种形式的唯一一件事就是长度未知的数组,例如extern double d[];,或类似的长度。限制了可用性。)
James Kanze 2014年

6

如果只想修改元素:

void foo(double *bar);

足够。

如果要将地址修改为(例如:)realloc,但不适用于数组:

void foo(double *&bar);

是正确的形式。


4
除了不能将数组传递给后者。他问的是数组,而不是指针。
James Kanze 2012年

@JamesKanze:是的。第二种情况不适用于堆栈数组,在这种情况下,他应该使用第一种形式。
纳斯塔2012年

8
第二种情况不适用于数组period。仅当您具有指针类型的实际变量时,它才有效。
James Kanze 2012年


3

8.3.5.8如果参数的类型包括“指向T的未知边界的数组的指针”或“指向T的未知边界的数组的指针”形式的类型,则程序格式错误


2

就像其他答案说的那样,在&后面加上*

这提出了一个有趣的观点,有时可能会造成混淆:类型应该从右到左读取。例如,这是(从最右边开始*)一个指向int的常量指针的指针。

int * const *x;

因此,您编写的内容将是指向引用的指针,这是不可能的。


更改顺序仍然不允许他传递数组。它需要一个实际的指针对象,而不是转换结果。
James Kanze 2012年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.