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*
,但我无法让它工作。
答案 0 :(得分:5)
使用offsetof
中的<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:
宏
offsetof
( type , member-designator )在本国际标准中接受一组受限制的类型参数。如果 type 不是标准布局类(第9条),则结果未定义。 195 表达式offsetof
( type , member-designator )从不依赖于类型(14.6.2.2),并且当且仅当 type 依赖时,它才是值依赖的(14.6.2.3)。将offsetof
宏应用于作为静态数据成员或函数成员的字段的结果是未定义的。offsetof
宏调用的任何操作都不会抛出异常,noexcept(offsetof(type, member-designator))
应为true
。
和C99(N1256)7.17 / 3:
宏是
NULL
扩展为实现定义的空指针常量;和
offsetof(type, member-designator)
扩展为一个整型常量表达式,其类型为
size_t
,其值是以字节为单位的偏移量,由结构成员(由 member-designator 指定),来自结构的开头(由 type 指定)。类型和成员指定符应为static type t;
然后表达式
&(t.
member-designator)
计算为地址常量。 (如果指定的成员是位字段,则行为未定义。)
C ++标准中的“本国际标准中的限制类型参数集”是为了引起您注意offsetof
比C标准更具限制性的事实。
答案 1 :(得分:3)
offsetof
给出了字符中的偏移量,因此在进行指针运算之前,需要将mStringMember
强制转换为char *
。
(Data*)((char*)mStringMember - offsetof(Data, b))
答案 2 :(得分: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);
}
答案 3 :(得分:1)
如果您确定某些ptr
实际上是某些s->b
的地址(并非总是如此),您可能会尝试使用offsetof:
Data* getBaseDataPtrFromString(std::string* ptr) {
void* ad = (char*)ptr - offsetof(Data,b);
return reinterpret_cast<Data*>(ad);
}
BTW,GCC有一个builtin_offsetof来帮助实现offsetof
宏(特别是在比标准要求的更一般的情况下)。请参阅this question。