3
struct Data {
    int a;
    std::string b;
    float c;
};

std::string* allocateDataAndGetString() {
    Data* dataPtr(someAllocator.allocate<Data>());
    return &dataPtr.b;
}

Data* getBaseDataPtrFromString(std::string* mStringMember) {
    // ???
}

int main() {
    std::string* stringPtr(allocateDataAndGetString());
    Data* dataPtr(getBaseDataPtrFromString
}

Data在堆上分配了一个实例,以及一个指向其std::string b;成员的指针。如何Data以标准方式获取字符串所属实例的基地址,同时考虑偏移和填充?

我试过从指针中减去sizeof(int)和,但我无法让它工作。std::offsetof(Data, std::string)std::string*

4

4 回答 4

5

使用offsetoffrom <cstddef>,但要注意它仅在标准布局类型(Live at Coliru)上定义:

Data* getBaseDataPtrFromString(std::string* mStringMember) {
    static_assert(std::is_standard_layout<Data>::value,
                  "offsetof() only works on standard-layout types.");
    return reinterpret_cast<Data*>(
      reinterpret_cast<char*>(mStringMember) - offsetof(Data, b)
    );
}

offsetof在 C++11 18.2/4 中有详细说明:

offsetoftypemember-designator)接受本国际标准中的一组受限类型参数。如果type不是标准布局类(第 9 条),则结果未定义。195表达式offsetof( type , member-designator ) 从不依赖于类型 (14.6.2.2),当且仅当类型依赖于时,它才依赖于值 (14.6.2.3) 。将offsetof宏应用于作为静态数据成员或函数成员的字段的结果是未定义的。宏调用的任何操作都不offsetof应引发异常,并且noexcept(offsetof(type, member-designator))应为true.

和 C99 (N1256) 7.17/3:

宏是

NULL

它扩展为实现定义的空指针常量;和

offsetof(type, member-designator)

它扩展为具有 type 的整数常量表达式size_t,其值是以字节为单位的偏移量,从其结构的开头(由type指定)到结构成员(由member-designator 指定)。类型和成员代号应使给定的

static type t;

然后表达式&(t.member-designator)计算为地址常量。(如果指定的成员是位域,则行为未定义。)

C++ 标准中的“本国际标准中的限制类型参数集”是为了提醒您注意offsetof比 C 标准更具限制性的事实。

于 2014-02-20T14:05:30.987 回答
3

offsetof给出 chars 中的偏移量,因此您需要在进行指针运算之前转换为mStringMemberto 。char *

(Data*)((char*)mStringMember - offsetof(Data, b))
于 2014-02-20T13:58:01.403 回答
2

offsetof仅适用于标准布局类型。

您可能可以使用一个技巧并且不需要标准布局:static_cast知道如何从指向其基数之一的指针获取更多派生对象。

struct Data : private std::string {
private:
    using b_base = std::string;
public:
    // was int f() const { return b.size(); }
    int f() const { return b_base::size(); }

private:
    int a;
    float c;
    friend Data* getBaseDataPtrFromString(std::string* mStringMember);
};

Data* getBaseDataPtrFromString(std::string* mStringMember) {
    return static_cast<Data*>(mStringMember);
}
于 2014-02-20T14:39:25.627 回答
1

如果您确定 someptr 实际上是 some 的地址s->b(并非总是如此),您可以尝试使用offsetof

Data* getBaseDataPtrFromString(std::string* ptr) {
   void* ad = (char*)ptr - offsetof(Data,b);
   return reinterpret_cast<Data*>(ad);
}

顺便说一句,GCC 有一个builtin_offsetof来帮助实现offsetof宏(特别是在比标准要求的更普遍的情况下)。看到这个问题

于 2014-02-20T13:58:12.577 回答