容器中不同对象的成员之间的指针差异是否相同?
is the pointerdiff between members of different objects in containers the same?
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;
}
}
}
what I wondered is whether all elements stored in a range share the same layout
当然可以,否则遍历多个相同类型的元素并访问每个元素的相同成员是不可能的。给定成员的偏移量是相对于元素的类型的。该偏移量对于该类型的 all 个实例是相同的。因此,一个类型中所有成员的组合构成了该类型的布局,并且该布局在该类型的所有使用中保持一致。
但是,您对成员偏移量的处理完全错误。您正在通过从 int*
指针中减去 T*
指针来计算偏移量,
- 甚至不应该编译,因为你不能减去不同类型的指针。
- 即使它编译了,也不会在
T
. 内为您提供 member
的正确 byte 偏移量
然后您将该偏移量应用于 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;
}
}
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;
}
}
}
what I wondered is whether all elements stored in a range share the same layout
当然可以,否则遍历多个相同类型的元素并访问每个元素的相同成员是不可能的。给定成员的偏移量是相对于元素的类型的。该偏移量对于该类型的 all 个实例是相同的。因此,一个类型中所有成员的组合构成了该类型的布局,并且该布局在该类型的所有使用中保持一致。
但是,您对成员偏移量的处理完全错误。您正在通过从 int*
指针中减去 T*
指针来计算偏移量,
- 甚至不应该编译,因为你不能减去不同类型的指针。
- 即使它编译了,也不会在
T
. 内为您提供
member
的正确 byte 偏移量
然后您将该偏移量应用于 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;
}
}