这段代码发生了什么?
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; }
这更容易理解。而且不需要宏。
我正在尝试使用 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; }
这更容易理解。而且不需要宏。