从C++标准:
标准布局类是具有以下特性的类:
— 没有非标准布局类类型的非静态数据成员(或 此类类型的数组)或引用,
— 没有虚函数 (10.3) 也没有虚拟基类 (10.1),
— 对所有非静态数据成员具有相同的访问控制(第 11 条), — 没有非标准布局基类,
— 要么在派生最多的类中没有非静态数据成员,要么 最多一个具有非静态数据成员的基类,或者没有基 具有非静态数据成员的类,以及
— 没有与第一个非静态数据相同类型的基类 成员
宏偏移量(类型,成员指示符)接受受限集 本标准中的类型参数。如果类型不是 标准布局类(条款 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;
}
offsetof
不能用于非标准布局类,因为它们在内存中的布局是未知的。例如,该标准未指定如何实现虚拟成员函数。一种常见的方法是添加指向 vtable 的指针作为类的第一个数据成员,但这不是唯一的方法。
至于你对offsetof
的定义:不能保证空指针通过reinterpret_cast
(或通过 C 样式强制转换)转换为0
,也没有为转换为整数的指针的其他值指定任何语义。
因此,如果您知道您的定义在编译器用于您的平台的底层寻址方案中有意义,那么它就可以工作。但这是一个如果你必须意识到的话。
答案是在模板中使用偏移量是完全安全的。因此不会造成任何伤害。但是,如果选择这样做,则会对模板的参数类型施加限制。它将在标准布局类中正常工作,原则上至少编译器应该告诉您参数何时属于它不起作用的类型。
根据标准,无论是否涉及任何模板,都无法获取非标准布局类成员的偏移量。它可能适用于单个编译器,但可能不会。它可能适用于所有非虚拟类(尽管这不是标准的要求)。也许你只需要尝试一下。
我们经常被迫编写不符合标准的代码来解决这样的问题,因此我们在各个编译器上对其进行了仔细测试。这只是意味着在研究和测试方面更加努力。