Должен ли класс быть стандартным типом макета, чтобы быть уверенным в смещениях памяти его членов?

Допустим, я хочу написать навязчивый список. У меня есть шаблон класса навязчивого списка, который принимает тип и указатель на член для использования в качестве узла. Это выглядит примерно так:

// 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

Решение

Я не могу процитировать стандарт для этого (и я почти уверен, что это undefined-поведение-land), но вы, как правило, можете полагаться на согласованность смещения члена данных относительно указателя на содержащий класс, при условии, что статический тип этого указателя является постоянным (IntegerListNode * в вашем случае).

Вы можете обнаружить, что смещение изменяется при его измерении относительно указателя на производный тип, но если вы всегда переинтерпретируете сначала в IntegerListNode и только потом статическое / динамическое приведение к производному типу, при желании, я почти чувствую себя комфортно с ним 🙂

Это не значит, что это хорошая идея. Такая арифметика указателей не требуется для реализации навязчивого списка. Если вы просто определяете указатель «следующий» и / или «предыдущий» в IntegerListNode (указывающий на IntegerListNode), вы можете передать указатель на член для этого в IntrusiveList, и вам никогда не понадобятся творческие преобразования 🙂

1

Другие решения


По вопросам рекламы [email protected]