在 C++ 中, 指针运算非常常见。 比如有一个32字节的 Buffer, 知道首地址和长度就可以管理整个 Buffer.
struct Buffer
{
unsigned char* head;
int size;
};
但是这段代码不够健壮, 仔细琢磨一下就能发现。 在 32 位机器上, unsigned char*
指针变量的长度是 4 字节, 而在 64 位机器上, 它的长度是 8 字节。
现在我们想访问 Buffer 的最后一个字节, 可以使用 unsigned char* tail = head + size - 1;
来实现。 但在 64 位环境下, 如果 size > 2147483647 字节,这个表达式就失效了。 如果把 size
变量改为 long long
型, 就可以了。
所以, 为了同时兼容 32 位 平台与 64 位平台, size 的类型不能是固定的。 这时, 就要用到模板元编程技巧来动态确定这个类型。 在工程上,这个类型叫做 PtrDiff
。
下面我们就来实现这个功能。 我们需要写一个函数, 它的输入是 int 型, 而输出是一个类型。 当指针长度为 4 字节时, 返回int
型; 当指针长度为 8 字节时, 返回 long long
(考虑跨平台的话, 在 MSVC 编译器下返回 __int64
型, 在 GCC 编译器下返回 long long
型)。 元编程代码一般使用Internal
命名空间与其他代码隔离。
namespace Internal
{
typename <int> struct IntegerForSize;
typename<> struct IntegerForSize<4>
{
using Signed = int;
using Unsigned = unsigned int;
}
typename<> struct IntegerForSize<8>
{
using Signed = long long;
using Unsigned = unsigned long long;
};
}
这段代码等同于
[Signed, Unsigned] IntegerForSize(int size)
{
Type Signed;
Type Unsigned;
if (size == 4)
{
Signed = int; Unsigned = unsigned int;
}
if (size == 8)
{
Signed = long long; Unsigned = unsigned long long;
}
return [Signed, Unsigned];
}
现在, 使用这段代码就可以计算出适当的类型了。
using PtrDiff = Internal::IntegerForSize<sizeof(void *)>::Signed;
为了方便使用, 可以给它做一层封装。 在模板元编程中, 元函数的封装, 一般通过继承实现。
namespace Internal
{
template <typename T>
struct IntegerForSizeOf : IntegerForSize<sizeof(T)> {};
}
using PtrDiff = Internal::IntegerForSizeOf<void*>::Signed;
最后, 把 Buffer
类的代码稍作修改即可。
struct Buffer
{
unsigned char* head;
PtrDiff size;
};