constexpr адрес базового класса

Используя clang 3.4 (trunk), есть ли способ рассчитать смещение базового класса с помощью постоянного выражения?

struct A { int a; };
struct B { int b; };

struct C: A, B {};

// cannot access base class of null pointer:
constexpr auto c_b_address = /*(size_t)*/ &(B&) *(C*)nullptr;

6

Решение

Да, Можно рассчитать смещение базового класса с помощью константного выражения, но это совсем не переносимо. Вы можете использовать малоизвестный, но документированный Расширение gcc, которое также поддерживается clang. Это предполагает использование __builtin_constant_p при использовании с оператором ?::

#define CB (&(B&)*(C*)nullptr)
constexpr auto c_b_address = __builtin_constant_p CB ? CB : CB;

Обратите внимание, что я только что использовал макрос CB, чтобы прояснить, что происходит. Конечно, это также можно сделать, повторив выражение несколько раз. Кстати, я впервые узнал об этом трюке в этом вопросе который содержит полезную справочную информацию.

Основная проблема, как вы, наверное, уже поняли, заключается в том, что ни reinterpret_cast ни эквивалентный бросок в стиле C не допускается в constexpr выражения. Любопытно, что бросок в стиле C (как указано выше) принят, но reinterpret_cast (что бы не генерировать код) нет. Я также экспериментировал с неясным, но, казалось бы, уместным ->* оператор, но со смешанными результатами:

#define CB1 (&(B&)*(C*)nullptr)
#define CB2 (&((reinterpret_cast<C*>(nullptr))->*(&C::b)))
#define CB3 (&(((C*)(nullptr))->*(&C::b)))
#define CB4 (&(B&)*reinterpret_cast<C*>(nullptr))
#define CB CB1

Результаты с g ++ 4.8.3 и clang ++ 3.4:

     g++             clang++
--- ------------    --------
CB1  OK              OK
CB2  compile error   compile error
CB3  OK              compiles but gives answer = 0
CB4  compile error   compile error

Только на моей 64-битной машине под управлением Linux CB1 дает правильный ответ 4 с обоими компиляторами. С gcc оба CB1 а также CB2 работать с или без __builtin_constant_p, Используя clang, единственная работающая версия была CB1 с __builtin_constant_p,

Почему мы должны верить, что это намеренное поведение?

Как @ShafikYaghmour довольно разумно спрашивает в комментарии: «У вас есть ссылка на gcc или clang, которая говорит, что они поддерживают это поведение?» Я собираюсь расширить это, чтобы спросить «какая документация существует, которая указывает, что это преднамеренно, а не просто странный побочный эффект?» В конце концов, если кто-то действительно собирается использовать это, было бы неплохо иметь какое-то указание на то, что оно может продолжать существовать в будущем. Этот раздел пытается решить эту проблему.

Для Clang, ссылка сам исходный код в котором комментарий в VisitConditionalOperator функция, которая говорит:

// If the condition (ignoring parens) is a __builtin_constant_p call,
// the result is a constant expression if it can be folded without
// side-effects. This is an important GNU extension. See GCC PR38377
// for discussion.

Это, в свою очередь, указывает на Bugzilla GCC ошибка 38377 который обсуждает эту проблему. В частности, в 2008 году эта ошибка была зарегистрирована как «__builtin_constant_p (t)? T: 1 не считается константным целочисленным выражением». В обсуждении отмечается, что для условного оператора (?:),

Да, это (задокументированный) особый случай, необходимый для совместимости с
существующий код GNU C.

И далее,

Если вы ошиблись в C, то GCC не сможет загрузиться, так как это часть семантики GNU C, на которую GCC полагается при сборке с GCC.

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

Тем не менее, применяются все обычные предостережения об использовании нестандартных деталей реализации. Если вы можете выполнить это во время выполнения, это станет приемлемым как для gcc, так и для clang:

ptrdiff_t cb = (char *)(&(B&)*(C*)nullptr) - (char *)nullptr;
5

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

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector