这段C++代码是什么意思?

22
以下代码返回堆栈分配数组的大小:
template<typename T, int size>
int siz(T (&) [size])
{
    return size;
}

但是我无法理解语法。特别是T (&) [size]这部分...


1
可能是为什么在C++中“数组引用”的定义如此混乱?的重复问题。 - sharptooth
7
不完全是重复的。 - Jason S
你可能想使用别名模板,并编写 alias<T[size]> &,这样更容易阅读。 - Johannes Schaub - litb
3个回答

24
但是我无法理解语法。特别是T(&)[size]这部分...
那部分是对数组的引用。有一个“右左规则”可以解密任何C和C++声明。
因为函数模板从提供的函数参数推断模板参数类型,所以这个函数模板的作用是推断数组的类型和元素计数,并返回计数。
函数不能按值接受数组类型,而只能按指针或引用接受。引用用于避免将数组隐式转换为其第一个元素的指针(也称为数组衰减):
void foo(int*);

int x[10];
int* p = x; // array decay
foo(x);     // array decay again

数组衰减会破坏数组的原始类型,因此其大小会丢失。

需要注意的是,在C++03中,由于它是一个函数调用,返回值不是编译时常量(即返回值不能用作模板参数)。在C++11中,可以使用constexpr标记该函数以返回编译时常量:

template<typename T, size_t size>
constexpr size_t siz(T(&)[size]) { return size; }

为了在C++03中将数组元素计数作为编译时常量获取,可以使用稍微不同的形式:
template<class T, size_t size>
char(&siz(T(&)[size]))[size]; // no definition required

int main()
{
    int x[10];
    cout << sizeof siz(x) << '\n';
    double y[sizeof siz(x)]; // use as a compile time constant 10
}

在上面的代码中,声明了一个具有相同引用数组参数的函数模板,但返回值类型为char(&)[size](这就是“右左规则”的体现)。请注意,函数调用从未发生在运行时,这就是为什么不需要定义函数模板siz的原因。sizeof siz(x)基本上是在说“如果调用siz(x),返回值的大小将是多少”
获取数组元素计数作为编译时常量的旧C/C++方式是:
#define SIZ(arr) (sizeof(arr) / sizeof(*(arr)))

需要在 & 周围加括号吗? - Jason S
@JasonS - 括号是必需的,以避免看起来像对T引用的数组。 - Flexo
啊,好的... (T&)[size] 也不行吗?(抱歉,我没有一个正在运行的 C 编译器) - Jason S
1
@JasonS 不,这与只是 T& 是一样的。括号明确表示 & 不是指数组元素的类型。(尽管 C 编译器无法进行测试 - C 中没有引用!) - Flexo
这是一个很棒的答案,特别是关于constexpr和旧的做法的额外信息。 - anton.burger
1
@awoodland:啊!灯终于亮了。所以应该引用T数组而不是引用数组。 - Jason S

7

T (&) [size]是一个数组的引用。它需要是一个引用,因为下面的程序是不合法的:

#include <iostream>

int sz(int *) { std::cout << "wtf?" << std::endl; return 0; }


int sz(int [4]) { std::cout << "4" << std::endl; return 0; }

int main() {
  int test[4];
  sz(test);
}

这个程序编译失败,显示如下错误:

test.cc: In function ‘int sz(int*)’:
test.cc:6:5: error: redefinition of ‘int sz(int*)’
test.cc:3:5: error: ‘int sz(int*)’ previously defined here

因为 int sz(int [4])int sz(int *) 是一样的。

这里需要括号来消除歧义,因为 T& [size] 看起来像是一个引用数组,但实际上是非法的。

通常情况下,如果参数不是匿名的,你应该写:

template<typename T, int size>
int sz(T (&arr) [size])

为数组命名为arr。在这种情况下,所有示例代码都关心的只是推断出的大小,因此匿名参数避免了有关未使用参数的警告。

4

这是一个函数,它可以成为一个类型名称(模板可以用不同的类型名称),并从外部获取其大小。然后返回这个大小。

栈函数通常使用“size”,它是一个整数,显示您使用此函数请求的堆栈大小。符号“&”用于测试T堆栈的大小。


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