Как сделать статическое утверждение, что приведение указателя тривиально? - PullRequest
7 голосов
/ 04 апреля 2011

Допустим, у меня есть эти типы:

struct A {
    int a;
};

struct B {
    int b;
};

struct C : public A, public B {
    int c;
};

C* указатель может быть приведен к A* указателю без корректировки фактического адреса вообще.Но когда C* приведен к B*, значение должно измениться.Я хотел бы убедиться, что два связанных типа, которые у меня есть, могут быть приведены друг к другу без изменения адреса (то есть, что нет множественного наследования или что базовый класс является первой базой производного класса).Это можно проверить во время выполнения, например, так:

assert(size_t(static_cast<A*>((C*)0xF000) == 0xF000);
assert(size_t(static_cast<B*>((C*)0xF000) != 0xF000);

Это работает.Но эта информация известна во время компиляции, поэтому я ищу способ сделать это во время компиляции.Очевидные способы преобразования приведенного выше в статическое утверждение (например, замена assert на BOOST_STATIC_ASSERT дает ошибку «приведение к типу, отличному от целочисленного или перечислимого типа, не может появляться в выражении-константе» с g ++ 4.2.

Переносимость не так уж важна. Использование расширений gcc или хакерских хитростей с шаблонами было бы хорошо.

Обновление: Обнаружено, что ранее задавался почти тот же вопрос: C ++, статически определять базовые классы с разными адресами? . Использование offsetof() также является единственным полезным предложением там.

Ответы [ 2 ]

4 голосов
/ 04 апреля 2011

«Я хотел бы убедиться, что два связанных типа можно приводить друг к другу без изменения адреса (то есть, что нет множественного наследования или что базовый класс является первой базой производного класса).»

Ваше "то есть" не правильно Например, вполне возможно, что первые 4 байта Derived являются указателями vtable, даже если Base не является полиморфным. Да, компиляторы C ++ проще, если (1) первый базовый подобъект находится со смещением 0, и (2) указатель vtable находится со смещением 0. Но эти две цели по своей сути не совпадают, и лучшего выбора нет.

Теперь первая часть может быть проверена теоретически. С типами стандартной компоновки не будет никакой разницы в offset_of(Base, first_member) и offset_of(Derived, first_member). Но на практике offset_of не работает для интересных типов; это UB. И весь смысл этой проверки в том, чтобы проверить тип, поэтому он должен надежно завершиться неудачей для нестандартных типов компоновки.

1 голос
/ 04 апреля 2011

Основываясь на предложении MSalters и ответе C ++, статически определять базовые классы с разными адресами? , вот наиболее близкая вещь к ответу, который я могу придумать. Он, вероятно, специфичен для gcc и требует знания некоторого члена базового класса:

#pragma GCC diagnostic ignored "-Winvalid-offsetof"     // To suppress warning.
BOOST_STATIC_ASSERT(offsetof(C, a) == offsetof(A, a));
BOOST_STATIC_ASSERT(offsetof(C, b) != offsetof(B, b));
#pragma GCC diagnostic warn "-Winvalid-offsetof"

Очевидно, что это неудобно и страшно (требуется знать участника и отключить предупреждение).

...