c++宏offsetof
只是在标准布局类型上使用时定义的行为。据我所知,这是因为编译器可以根据其运行的代码的上下文来更改数据的内存布局。(例如,从不使用变量时(
然而,我想知道的是,存储在一个范围中的所有元素是否共享相同的布局。或者,换句话说,如果以下代码定义良好:
template<typename T>
concept has_member_int = requires(const T& x)
{
{ x.member } -> std::same_as<int>;
};
template <std::ranges::Range Range_t, has_member_int T>
void setEveryMemberTo20(Range_t<T> range)
{
if (range.size() > 0)
{
auto& firstElement = *(range.begin());
auto ptrdiffToMember = &(firstElement.member) - &firstElement;
for (auto& element : range)
{
*(reinterpret_cast<int*>(&element + ptrdiffToMember)) = 20;
}
}
}
我想知道的是存储在一个范围中的所有元素是否共享相同的布局
当然可以,否则就不可能遍历同一类型的多个元素并访问每个元素的同一成员。给定成员的偏移量是相对于元素的类型的。对于该类型的所有实例,该偏移量是相同的。因此,一个类型中所有成员的组合构成了该类型的布局,并且该布局在该类型的所有使用中保持一致。
但是,您对成员偏移量的处理完全错误。您通过从int*
指针中减去T*
指针来计算偏移量,该
- 甚至不应该编译,因为不能减去不同类型的指针
- 即使它确实进行了编译,也不会在
T
中为member
提供正确的字节偏移量
然后将该偏移量应用于T*
指针,这将使指针前进那么多T
实例,而不是那么多字节。IOW,如果T
中member
的偏移量为4,则表示T*
指针前进了sizeof(T) * 4
字节,而不是仅前进了4字节。
我认为您需要复习指针算术的实际工作原理。
试试类似的东西:
auto& firstElement = *(range.begin());
// or: T& firstElement = ...
auto ptrdiffToMember = reinterpret_cast<uintptr_t>(&firstElement.member) - reinterpret_cast<uintptr_t>(&firstElement);
// or: auto ptrdiffToMember = offsetof(T, member);
for (auto& element : range)
{
*(reinterpret_cast<int*>(reinterpret_cast<uintptr_t>(&element) + ptrdiffToMember)) = 20;
}
但是,正如@alterigel在评论中所说,只需使用element.member = 20;
即可。你根本不需要处理指针操作:
template <std::ranges::Range Range_t, has_member_int T>
void setEveryMemberTo20(Range_t<T> range)
{
for (auto& element : range)
{
element.member = 20;
}
}