如何在编译时计算类成员的偏移量?

Jun*_*uan 15 c++ templates metaprogramming member offset

给定C++中的类定义

class A
{
  public:
    //methods definition
    ....

  private:
    int i;
    char *str;
    ....
}
Run Code Online (Sandbox Code Playgroud)

是否可以使用C++模板元编程在编译时计算类成员的偏移量?该类不是POD,并且可以具有虚方法,原始数据和对象数据成员.

Gle*_*Low 10

基于Matthieu M.的答案但更短且没有宏:

template<typename T, typename U> constexpr size_t offsetOf(U T::*member)
{
    return (char*)&((T*)nullptr->*member) - (char*)nullptr;
}
Run Code Online (Sandbox Code Playgroud)

它被称为这样:

struct X { int a, b, c, d; }

std::cout << "offset of c in X == " << offsetOf(&X::c);
Run Code Online (Sandbox Code Playgroud)

编辑:

杰森赖斯是对的.这不会在C++ 11中产生实际的常量表达式.鉴于http://en.cppreference.com/w/cpp/language/constant_expression中的限制,它看起来不可能- 特别是没有指针差异,并且reinterpret_cast可以在常量表达式中.

  • 这通过解除引用空指针导致未定义的行为,并且当两个指针都不指向同一对象时也通过减去指针 (2认同)

Mat*_* M. 5

嗯...在C ++ 11中,您实际上可以使用常规C ++工具来计算此类偏移量(即,无需委派给特定的编译器固有函数)。

在实际工作空间中

template <typename T, typename U>
constexpr int func(T const& t, U T::* a) {
     return (char const*)&t - (char const*)&(t.*a);
}
Run Code Online (Sandbox Code Playgroud)

但是,这里依赖于tconstexpr实例的引用,这可能不适用于所有类。只要它是构造函数,它就不会禁止T使用virtual方法,甚至构造constexpr函数。

尽管如此,这仍然是一个障碍。在未评估的上下文中,我们实际上可以std::declval<T>()用来模拟拥有一个对象。一无所有。因此,这对对象的构造函数没有特殊要求。另一方面,我们可以从这样的上下文中提取的值很少...而且它们的确也对当前的编译器造成了问题。

在实际工作空间中

template <typename T, typename U>
constexpr size_t offsetof_impl(T const* t, U T::* a) {
    return (char const*)t - (char const*)&(t->*a) >= 0 ?
           (char const*)t - (char const*)&(t->*a)      :
           (char const*)&(t->*a) - (char const*)t;
}

#define offsetof(Type_, Attr_)                          \
    offsetof_impl((Type_ const*)nullptr, &Type_::Attr_)
Run Code Online (Sandbox Code Playgroud)

我预见的唯一问题是virtual继承,因为它在基础对象上的运行时位置。如果存在其他缺陷,我将很高兴。

  • 另见内置 offsetof 宏 (2认同)

Yak*_*ont 4

不,一般来说不是。

offsetof 宏存在于 POD(普通旧数据)结构中,并且可以使用 C++0x 将其稍微扩展为标准布局结构(或其他类似的轻微扩展)。因此,对于那些受限的情况,您有一个解决方案。

C++ 为编译器编写者提供了很大的自由。我不知道有任何子句可以阻止某些类对类的成员具有变量偏移量——但是,我也不确定为什么编译器会这样做。;)

现在,保持代码标准兼容但仍具有偏移量的一种方法是将数据粘贴到 POD(或某些 C++0x 扩展)子结构中,offsetof 将在该子结构上工作,然后处理该子结构struct 而不是整个类。或者您可以放弃标准合规性。类中结构体的偏移量是未知的,但结构体中成员的偏移量是未知的。

一个重要的问题是“我为什么想要这个,我真的有充分的理由吗”?

  • @austin这是一个针对普通旧数据的情况:以这种方式将非标准布局类放入数据库将是一个坏主意。 (3认同)