2015-07-21 63 views
1

我正在尝试开发一个Android键盘,使用Android AOSP键盘来源作为模型。有相当多的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代码中没有其他地方出现或者生成文件(也就是说,就我可以告诉它它不是某个宏的宏而言)。

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

#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问题的另一个问题。感谢大家的解释!

+0

如果您为参数提供*指针*以及如何发出首个地址,请采取第二种方式尝试并考虑其*如何工作*(但不是您想如何)。就第一个而言,您可能希望提供您正在使用的C++编译器工具链信息,因为它可以与我过去十年来使用的几乎所有C++编译器一起工作([见它实时](http:// ideone.com/l3OWYN))。通常会引发第一个人的事情是,实际的模板功能不需要*实现*,但它仍然有效。 – WhozCraig

+0

你到底是如何编译它的? (较老的编译器可能在语法上有问题。) –

+0

@AlanStokes - 我正在使用Android NDK提供的标准工具链。 –

回答

4

此:

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

声明了一个名为ArraySizeHelper函数,它以命名arrayNT秒的数组的引用,并返回到的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; } 

这是远远更容易理解。并不需要宏。

+0

我同意今天用'constexpr'做这个评估。唉,几乎所有OP的帖子的使用(我第一次在MS ATL 3.1中看到它)在长时间*之前都是语言功能。 (例如:您的简化不会在VS2008上编译; OP的第一个代码*将*)。 – WhozCraig

+0

'constexpr'代码看起来不错,但是没用,因为标准禁止你传递一个实参(左值)。愚蠢。是! –

+0

@ Cheersandhth.-Alf平淡无味,因为我非常喜欢那种简化。 – WhozCraig

4

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

简单定义

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

&hellip;是不安全的,因为你可以传递一个指针,并返回一个无意义的大小。

所以你的代码,

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

&hellip;使用模板参数推导到找到的大小,并且它使用引用到数组的返回类型来将大小报告为编译时间常量。如果不是(1)在C++ 11及以后版本中关于传递引用的愚蠢措辞,我们现在可以对constexpr执行相同的操作。唉,我们可能必须等到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 “ 甲条件表达式e芯常量表达式除非的e评价,继 抽象机的规则(1.9)将评估以下表达式之一:[&hellip;] - 一个id表达式,它引用引用类型的变量或数据成员,除非引用具有前面的初始化和 - 使用常量表达式或 进行初始化 - 它是一个对象的非静态数据成员,其对象的生命周期始于e的评估; ”

+0

感谢您的解释。我怀疑它可能与类型安全有关,但我的C++过于生疏,无法弄清楚发生了什么。 –