这段代码发生了什么?

What is happening with this code?

我正在尝试使用 Android AOSP 键盘源作为模型开发一个 Android 键盘。有很多 JNI 代码,我的 C++ 有点生疏,我在使用以下宏定义时遇到问题 NELEMS:

// Disclaimer: You will see a compile error if you use this macro against a variable-length array.
// Sorry for the inconvenience. It isn't supported.
template <typename T, int N>
char (&ArraySizeHelper(T (&array)[N]))[N];
#define NELEMS(x) (sizeof(ArraySizeHelper(x)))

当我尝试编译时,此代码的第二行(就在 #define 上方)亮起并显示错误:

Declaration of reference variable requires an initializer

错误信息对我来说有些道理; AOSP 代码没有。符号 ArraySizeHelper 在 AOSP 代码或 make 文件中没有出现(也就是说,据我所知,它不是其他东西的宏)。

根据宏的名称,我猜它应该计算数组中元素的数量。不过,据我所知,通常的做法是:

#define NELEMS(x) (sizeof(x) / sizeof((x)[0]))

所以我想知道这里是否发生了其他事情。

我希望能解释一下这段代码应该做什么,并指导如何处理编译错误。

编辑: 我正在通过 Android Studio 1.3 RC 3、Android NDK r10e 和 Gradle 2.5 进行编译。编译使用各种工具链(如 this Android documentation 中所述)。奇怪的是,上面的代码现在可以正确编译和执行(也许它总是这样)。但是,Android studio 仍然在该行显示错误。它还会在每次使用 NELEMS:

时显示错误

Error after macro substitution: Too many arguments, expected 0

我现在认为这是一个 IDE 代码分析错误,而不是编译器或编码问题。我最初的问题是关于代码本身的,所以我将此线程标记为已回答。我将打开另一个关于 IDE 问题的问题。感谢大家的讲解!

该代码的目的是安全地获取可在编译时使用的数组大小,例如作为新原始(非动态)数组的大小。

简单定义

#define NELEMS(x) (sizeof(x) / sizeof((x)[0]))

... 是不安全的,因为您可以将指针传递给它,并返回一个无意义的大小。

所以你的代码,

template <typename T, int N>
char (&ArraySizeHelper(T (&array)[N]))[N];
#define NELEMS(x) (sizeof(ArraySizeHelper(x)))

… 使用模板参数推导 找到 大小,并使用数组引用 return 类型将大小报告为编译时间常量。如果不是因为 (1)C++11 及以后关于传递引用的愚蠢措辞,我们现在可以对 constexpr 做同样的事情。 las,我们可能必须等到 C++17 才能完全避免使用宏来完成获取编译时数组大小的简单任务。


我无法重现问题;以下代码:

template <typename T, int N>
char (&ArraySizeHelper(T (&array)[N]))[N];
#define NELEMS(x) (sizeof(ArraySizeHelper(x)))

#include <iostream>
auto main() -> int
{
    using namespace std;
    int x[42];
    cout << NELEMS( x ) << endl;
}

可与 Visual C++ 2015 和 MinGW-64 g++ 5.1.0 完美编译。


1) C++14 §5.19/2 “A 条件表达式 e 是一个 核心常量表达式除非评估e,遵循规则 抽象机 (1.9),将评估以下表达式之一:[…] — id-expression 引用引用类型的变量或数据成员,除非引用具有先前的初始化和 — 它是用常量表达式初始化的,或者 — 它是一个对象的非静态数据成员,该对象的生命周期始于 e 的评估;”

这个:

template <typename T, int N>
char (&ArraySizeHelper(T (&array)[N]))[N];

声明了一个名为 ArraySizeHelper 的函数,它引用了一个名为 array N T 的数组,并且 returns 引用了一个数组char[N] 个。没有给出定义。

sizeof() 不需要定义函数 - 它的操作数未计算。它只对类型进行操作:所以如果 x 的类型是 T[N](否则不编译),sizeof(ArrayHelper(x)) 计算为 sizeof(char[N])

这也是一种极其复杂的写作方式:

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

这更容易理解。而且不需要宏。