С++, статически определять базовые классы с разными адресами?

Если у меня есть производный класс с несколькими базами, каждый указатель this для каждой базы будет отличаться от указателя this производного объекта, за исключением одного. Учитывая два типа в иерархии наследования, я хотел бы определить во время компиляции, используют ли они один и тот же указатель this. Что-то вроде этого должно работать, но не работает:

BOOST_STATIC_ASSERT(static_cast<Base1*>((Derived *)0xDEADBEEF) == (Derived*)0xDEADBEEF);

Потому что это должно быть «интегральное постоянное выражение», и в соответствии со стандартом разрешены только целочисленные приведения (что глупо, потому что им нужна информация о времени компиляции только в том случае, если виртуальное наследование не используется). Та же проблема возникает при попытке передать результаты как целочисленные параметры шаблона.

Лучшее, что я смог сделать, это проверить при запуске, но мне нужна информация во время компиляции (чтобы заставить работать какой-то глубокий хакерский шаблон).


person Joseph Garvin    schedule 15.07.2009    source источник
comment
им нужна только информация о времени компиляции, если в большинстве/всех реализациях не используется виртуальное наследование. Стандарт позволяет любому приведению не-POD использовать информацию времени выполнения.   -  person Steve Jessop    schedule 15.07.2009


Ответы (6)


Я не знаю, как проверить, чего вы не хотите, но обратите внимание, что ваше предположение неверно при наличии пустых базовых классов. Любое их количество может иметь одинаковое смещение от начала объекта, если они относятся к разным типам.

person AProgrammer    schedule 15.07.2009
comment
Немного подумав о вашей проблеме - и предполагая, что ваш вопрос касается смещения базовых классов, а не указателей - я бы предположил, что offsetof возвращает постоянное интегральное выражение и поэтому должен быть в состоянии выполнить проверку, если ваш можете ограничить себя POD. Но g++ жалуется, если я использую offsetof с производным классом, и мне интересно, что мне не хватает. - person AProgrammer; 15.07.2009
comment
@AProgrammer: вы упускаете то, что любой производный класс по определению не является POD? Поэтому OP не может использовать только классы POD. - person Steve Jessop; 15.07.2009
comment
К сожалению, я слишком часто просматривал текущий черновик (где стандартный макет, который является предварительным условием использования offsetof, может иметь не виртуальные базовые классы). - person AProgrammer; 15.07.2009

Я пытаюсь решить эту же проблему. У меня есть реализация, которая работает, если вы знаете, какая переменная-член находится в начале макета базового класса. Например. если переменная-член "x" существует в начале каждого класса, то следующий код будет работать, чтобы получить смещение в байтах определенного макета базового класса из макета производного класса: offsetof(derived, base2::x).

В случае:
struct base1 { char x[16]; };
struct base2 { int x; };
struct derived : public base1, public base2 { int x; };
static const int my_constant = offsetof(derived, base2::x);

Компилятор правильно назначит «16» для my_constant в моей архитектуре (x86_64).

Трудность состоит в том, чтобы получить «16», когда вы не знаете, какая переменная-член находится в начале макета базового класса.

person user437522    schedule 02.09.2010

Я даже не уверен, что это смещение вообще является константой. У вас есть нормативная формулировка, предполагающая обратное?

Я согласен с тем, что неконстантное смещение было бы чертовски сложно реализовать при отсутствии виртуального наследования и бессмысленно для загрузки. Это помимо сути.

person MSalters    schedule 15.07.2009

У классов нет указателя this - он есть у экземпляров классов, и он будет разным для каждого экземпляра, независимо от того, как они получены.

person Community    schedule 15.07.2009
comment
Однако он создает фальшивый указатель объекта и пытается использовать предположения о смещении базового класса, сделанные во время компиляции, чтобы сделать вывод, имеет ли он базовый класс или нет. Ему не нужен реальный объект - однако я думаю, что это слишком зависит от реализации. - person polyglot; 15.07.2009

Как насчет использования

BOOST_STATIC_ASSERT(boost::is_convertible<Derived*,Base*>::value)

как задокументировано в следующих местах...

http://www.boost.org/doc/libs/1_39_0/doc/html/boost_staticassert.html

http://www.boost.org/doc/libs/1_38_0/libs/type_traits/doc/html/boost_typetraits/reference/is_convertible.html

person polyglot    schedule 15.07.2009
comment
Даже если это не идеально, просмотр их реализации может вам помочь :) - person polyglot; 15.07.2009
comment
К сожалению, мне нужно фактическое смещение. Их реализация все еще может помочь мне... как только я это пойму;) Это довольно плотно. - person Joseph Garvin; 16.07.2009

Я не понимал, что компилятор вставит эту проверку во время выполнения, но ваше основное предположение не совсем верно. Вероятно, не так, как вам нужно: компилятор может использовать оптимизацию пустого базового класса, если вы наследуете более чем от одного базового класса с sizeof(base class)==0. Это приведет к (base class *)(derived *)1==at least one other base class.

Как я уже сказал, это, вероятно, не то, о чем вам действительно нужно заботиться.

person MSN    schedule 15.07.2009