0

当用于标准布局类型时,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;
    }
  }
}
4

1 回答 1

3

我想知道的是存储在一个范围内的所有元素是否共享相同的布局

他们当然会这样做,否则遍历相同类型的多个元素并访问每个元素的相同成员是不可能的。给定成员的偏移量是相对于元素类型的。对于该类型的所有实例,该偏移量都是相同的。因此,一个类型中所有成员的组合构成了该类型的布局,并且该布局在该类型的所有使用中保持一致。

但是,您对成员偏移量的处理都是错误的。您通过从指针中减去T*指针来计算偏移量int*,这

  • 甚至不应该编译,因为你不能减去不同类型的指针。
  • 即使它确实编译了,也不会给你正确的字节偏移量memberwithin T

然后您将该偏移量应用于T*指针,这将使指针前进那么多T实例,而不是那么多字节。IOW,如果memberwithin的偏移量为 4,则您将指针T推进字节,而不仅仅是 4 个字节。T*sizeof(T) * 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;
  }
}
于 2021-01-12T23:13:14.760 回答