从C标准:
标准布局类是一个类: - 没有类型非标准布局类(或此类类型的数组)或引用的非静态数据成员, - 没有虚函数(10.3)且没有虚基类(10.1), - 对所有非静态数据成员具有相同的访问控制(第11条), - 没有非标准布局基类, - 在最派生类中没有非静态数据成员,并且最多一个具有非静态数据成员的基类,或者没有带有非静态数据成员的基类,并且 - 没有与第一个非静态数据成员相同类型的基类宏offsetof(type,member-designator)在本国际标准中接受一组受限制的类型参数 . 如果type不是标准布局类(第9节),则结果是未定义的
考虑到这些陈述是否有任何安全的方法使用 offsetof
依赖于模板参数的成员?如果没有,我怎样才能获得模板类中成员的偏移量?使用类似以下内容时可能不安全:
//MS Visual Studio 2013 definition
#define offsetof(s,m) (size_t)&reinterpret_cast<const volatile char&>((((s *)0)->m))
在非标准布局类?
根据标准,样品不安全的样品:
#include <cstddef>
#include <iostream>
template<typename T>
struct Test
{
int a;
T b;
};
struct NonStdLayout
{
virtual void f(){};
};
int main()
{
std::cout << offsetof(Test<int>, b) << std::endl;
std::cout << offsetof(Test<NonStdLayout>, b) << std::endl;
return 0;
}
2 回答
offsetof
不能仅仅因为它们在内存中的布局未知而在非标准布局类上使用 . 例如,标准未指定虚拟成员函数的实现方式 . 一种常见的方法是将指向vtable的指针添加为类的第一个数据成员,但这不是唯一的方法 .至于你对
offsetof
的定义:不能保证空指针通过reinterpret_cast
(或通过C风格的转换)转换为0
,也没有为其他转换为整数的指针值指定任何语义 .所以 if 你知道你的定义在编译器为你的平台使用的底层寻址方案中是有意义的,它可以工作 . 但这是一个 if 你必须要注意 .
答案是在模板中使用offsetof是完全安全的 . 这样就不会造成伤害 . 但是,如果您选择这样做,则会对模板的参数类型施加限制 . 它可以正常用于标准布局类,并且原则上至少编译器应该告诉您何时参数属于不可用的类型 .
无论是否涉及任何模板,都无法在标准下获取非标准布局类成员的偏移量 . 它可能适用于个人编译器,但可能不适用 . 它可能适用于所有非虚拟类(尽管这不是标准的要求) . 也许你只需要进行实验 .
我们经常被迫编写不符合标准的代码来解决这类问题,因此我们会仔细测试各个编译器 . 这只意味着更多的研究和测试工作 .