简体   繁体   English

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

[英]How to calculate offset of a class member at compile time?

Given a class definition in C++ 给定C ++中的类定义

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

  private:
    int i;
    char *str;
    ....
}

Is it possible to calculate the offset of a class member at compile time using C++ template meta-programming? 是否可以使用C ++模板元编程在编译时计算类成员的偏移量? The class is not POD, and can have virtual methods, primitive and object data member. 该类不是POD,并且可以具有虚方法,原始数据和对象数据成员。

Based on Matthieu M.'s answer but shorter and with no macros: 基于Matthieu M.的答案但更短且没有宏:

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

And it's called like this: 它被称为这样:

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

std::cout << "offset of c in X == " << offsetOf(&X::c);

Edit: 编辑:

Jason Rice is correct. 杰森赖斯是对的。 This will not produce an actual constant expression in C++11. 这不会在C ++ 11中产生实际的常量表达式。 It doesn't look possible given the restrictions in http://en.cppreference.com/w/cpp/language/constant_expression -- in particular no pointer difference and reinterpret_cast can be in a constant expression. 鉴于http://en.cppreference.com/w/cpp/language/constant_expression中的限制,它看起来不可能 - 特别是没有指针差异, reinterpret_cast可以是常量表达式。

Well... in C++11, you actually can compute such offsets right with regular C++ facilities (ie, without delegating to a particular compiler intrinsic). 那么......在C ++ 11中,你实际上可以使用常规的C ++工具来计算这样的偏移(即,不需要委托给特定的编译器内部)。

In action at liveworkspace : liveworkspace的行动:

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

However this relies on t being a reference to a constexpr instance here, which might not be applicable to all classes. 然而,这依赖于t是一个参考constexpr实例在这里,这可能并不适用于所有类。 It does not forbid T from having a virtual method though, nor even a constructor, as long as it is a constexpr constructor. 它不禁止T拥有virtual方法,甚至也不构造构造函数,只要它是constexpr构造函数即可。

Still, this is quite a hindrance. 尽管如此,这仍是一个障碍。 In unevaluated contexts we could actually use std::declval<T>() to simulate having an object; 在未std::declval<T>()上下文中,我们实际上可以使用std::declval<T>()来模拟一个对象; while having none. 没有。 This poses no specific requirements on the constructor of an object, therefore. 因此,这对对象的构造函数没有特定要求。 On the other hand, the values we can extract from such context are few... and they do pose issues with current compilers, too... Well, let's fake it! 另一方面,我们可以从这样的上下文中提取的值很少......而且它们确实会对当前的编译器造成问题......好吧,让我们假装它!

In action at liveworkspace : liveworkspace的行动:

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_)

The only issue I foresee is with virtual inheritance, because of its runtime placement of the base object. 我预见的唯一问题是virtual继承,因为它基于对象的运行时放置。 I would be glad to be presented with other defects, if there are. 如果有的话,我会很高兴得到其他缺陷。

No, not in general. 不,不是一般的。

The offsetof macro exists for POD (plain old data) structs, and it may be extended slightly with C++0x to standard layout structs (or other similar slight extensions). 对于POD(普通旧数据)结构,存在offset的宏,并且可以使用C ++ 0x将其略微扩展到标准布局结构(或其他类似的轻微扩展)。 So for those restricted cases, you have a solution. 因此对于那些受限制的案例,您有一个解决方案。

C++ offers a lot of freedom to compiler writers. C ++为编译器编写者提供了很大的自由。 I don't know of any clause that would prevent some classes from having variable offsets to members of the class -- however, I'm not sure why a compiler would do that either. 我不知道任何会阻止某些类对类成员进行变量偏移的子句 - 但是,我不确定编译器为什么会这样做。 ;) ;)

Now, one approach to keep your code standards compliant, yet still have offsets, would be to stick your data into a POD (or some C++0x extension) sub-struct, on which offsetof will work, then work on that sub-struct instead of on the entire class. 现在,保持代码标准兼容但仍有偏移的一种方法是将数据粘贴到POD(或某些C ++ 0x扩展)子结构中,offsetof将在该子结构上工作,然后处理该子struct而不是整个类。 Or you could surrender standards compliance. 或者你可以放弃标准合规。 The offset of your struct within your class wouldn't be known, but the offset of the member within the struct would be. 您的类在您的类中的偏移量将是未知的,但结构中的成员的偏移量将是。

An important question to ask is "why do I want this, and do I really have a good reason"? 一个重要的问题是“我为什么要这样做,我真的有充分理由”吗?

In the 1996 book "Inside the C++ Object Model", written by Stanley B. Lippman, one of the original C++ designer's, makes reference to Pointer-to-Member Functions in Chapter 4.4 在1996年出版的“Inside the C ++ Object Model”一书中,由最初的C ++设计师之一Stanley B. Lippman编写,在第4.4章中引用了指向成员函数的指针。

the value returned from taking the address of a nonstatic data member is the byte value of the member's position in the class layout (plus 1). 获取非静态数据成员的地址返回的值是成员在类布局中的位置的字节值(加1)。 One can think of it as an incomplete value. 人们可以将其视为不完整的价值。 It needs to be bound to the address of a class object before an actual instance of the member can be accessed. 在访问成员的实际实例之前,需要将其绑定到类对象的地址。

While I vaguely recall that +1 from somewhere in some previous life, I've never seen or made use of this syntax before. 虽然我模糊地回忆起以前生活中某处的+1,但我以前从未见过或使用过这种语法。

class t
{
public:
    int i;
    int j;
};
int (t::*pmf)() = &t::i;

At least according to the description, this seems to be a cool way to get "almost" the offset. 至少根据描述,这似乎是获得“几乎”偏移的一种很酷的方式。

But It doesn't seem to work anymore, at least in GCC. 但它似乎不再起作用,至少在GCC中是这样。 I get a 我得到了

Cannot initialize a variable of type 'int (t::*) with an rvalue of type "int t:: *'

Does anybody have any history behind what is going on here? 有没有人在这里发生什么历史? Is something like this still possible? 这样的事情还有可能吗?

Problem with the web -- obsolete books never die... 网络问题 - 过时的书永远不会死...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM