Ответ 1
Следующее должно работать (кредиты идут на ответ на этот вопрос для этой идеи):
#include <cstddef>
template <typename T, typename M> M get_member_type(M T::*);
template <typename T, typename M> T get_class_type(M T::*);
template <typename T,
typename R,
R T::*M
>
constexpr std::size_t offset_of()
{
return reinterpret_cast<std::size_t>(&(((T*)0)->*M));
}
#define OFFSET_OF(m) offset_of<decltype(get_class_type(m)), \
decltype(get_member_type(m)), m>()
struct S
{
int x;
int y;
};
static_assert(OFFSET_OF(&S::x) == 0, "");
Обратите внимание, что в gcc макрос offsetof
расширяется до встроенного расширения, которое можно использовать во время компиляции (см. ниже). Кроме того, ваш код вызывает UB, он разделяет нулевой указатель, поэтому даже если он может работать на практике, нет никаких гарантий.
#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
Как отметил Люк Дантон, постоянные выражения не могут включать reinterpret_cast
в соответствии со стандартом С++ 11, хотя в настоящее время gcc принимает код (см. отчет об ошибке здесь). Кроме того, я нашел отчет о дефектах 1384, который
говорит о том, чтобы сделать правила менее строгими, поэтому это может измениться в будущем.