类是否需要是标准布局类型以确保其成员的内存偏移量?

时间:2015-05-26 05:08:24

标签: c++ intrusive-containers standard-layout

让我们说我想写一个侵入性列表。我有一个侵入式列表类模板,它接受类型和指向成员的指针用作节点。看起来大致如下:

// This needs to be a member of anything the intrusive list is going to store.
class IntrusiveListNode {
    // ...
}

// The intrusive list itself
template <class T, IntrusiveListNode T::*Member>
class IntrusiveList {
    // ...
};

// This is a type that is going to be stored in an intrusive list.
class IntegerListNode {
public:
    IntrusiveListNode node;

private:
    int value;
};

// An example of the how the list would be used.
IntrusiveList<IntegerListNode, &IntegerListNode::node> myList;

您要在列表中存储的每个内容都有一个IntrusiveListNode。要将IntrusiveListNode变回你可以使用的东西,比如IntegerListNode,你可以调用一个函数,该函数根据它在类中的偏移量对节点进行一些指针运算,然后将其强制转换为适当的类型。这似乎有效,但我认为不能保证。

我希望能够在我的类中添加一个static_assert,它在编译时验证您使用的类型是否安全,但我不确定static_assert的条件是什么。我认为只有在持有IntrusiveListNode的类型是标准布局类的情况下,这才能保证工作,但我不确定,因为标准布局类型的要求似乎比我实际需要的更严格。

特别是,标准布局类型要求所有成员具有相同的访问控制。我需要的是能够确保指针算术运行。这意味着你不能在多态类型上使用它,因为结构的两个不同版本的布局可能不同,但如果类型包含私有和公共数据成员的混合,这不应该是一个问题,对吧?如果我只是要求类型是非多态的,那会安全吗?或者有更好的检查吗?还是我坚持做is_standard_layout检查?

1 个答案:

答案 0 :(得分:1)

我不能引用这个标准(我很确定这是undefined-behavior-land),但你通常可以依赖于数据成员相对于指向包含类的指针的偏移量的一致性。该指针的静态类型是常量(在您的情况下为IntegerListNode *)。

您可能会发现偏移在相对于指向派生类型的指针进行测量时会发生变化,但只要您始终首先将reinterpret_cast重新设置为IntegerListNode,然后根据需要将静态/动态强制转换为派生类型,我几乎会感觉到很舒服:)

这并不是说这是一个好主意。实现侵入式列表不需要这种指针算术。如果您只是在IntegerListNode(指向IntegerListNode)中定义“next”和/或“previous”指针,则可以将指向该成员的指针传递给IntrusiveList,并且永远不需要创意转换:)