我们一般在程序中计算数组类型变量的大小通过定义如下的一个宏实现,如
NUM_ELEMENTS
定义:
#define NUM_ELEMENTS(x) (sizeof((x))/sizeof((x)[0]))
该宏在计算正确的数组类型变量时不存在问题,但应用到指针上或应用到支持
[]
重载的类时都会有问题。
通过
C++
实现一种安全高效的数组大小运算符
dimensionof()
,以下是该运算符的实现:
template <int N>
struct array_size_struct
{
byte_t c[N];
};
template <class T, int N>
array_size_struct<N> static_array_size_fn(T (&)[N]);
#define dimensionof(x) sizeof(static_array_size_fn(x).c)
原理:声明一个模板函数
static_array_size_fn()
,它接受一个元素类型为
T
、大小为
N
的数组的引用,
T
和
N
分别作为它的两个模板参数,这样一来,指针以及用户自定义类型就拒之门外了,该函数返回
array_size_struct
类模板的一个实例,该类模板以
static_array_size_fn()
接受的数组大小来实参化,且内部包含一个对应大小的字节数组。
dimensionof()
宏则简单地将
sizeof
操作符应用到
static_array_size_fn
返回的实例中的数组成员身上,从而得到数组大小。
该
dimensionof()
总是在编译期被求值,因此
dimensionof()
可以被用在常量可以使用的任何地方。
C++
标准中,
sizeof
的操作数不会被求值,所以无需产生
static_array_size_fn()
函数体,从而该设施是零代价的,没有任何运行期开销,也不会代码膨胀。
测试代码如:
char cArray[40];
char *pCArray = cArray;
vector<char> vecChar(50);
size_t c_cArray = NUM_ELEMENTS(cArray); // Ok
size_t c_pCArray = NUM_ELEMENTS(pCArray); //
编译通过,执行异常
size_t c_vecChar = NUM_ELEMENTS(vecChar); //
编译通过,执行异常
c_cArray = dimensionof(cArray); // Ok
c_pCArray = dimensionof(pCArray); //
编译不能通过
c_vecChar = dimensionof(vecChar); //
编译不能通过
如例:对于危险的调用,利用上面的方式,在编译期就可以把异常隔离起来,
C++
模板元编程的功劳。现在可以方便的把
dimensionof
运算符应用到你的工程里了。
注:参考《
Imperfect C++
》。