У меня есть необходимость использовать offsetof
из template
с селектором члена. Я придумал способ, если вы простите неловкий синтаксис:
template <typename T,
typename R,
R T::*M
>
constexpr std::size_t offset_of()
{
return reinterpret_cast<std::size_t>(&(((T*)0)->*M));
};
Использование не идеально (раздражает в лучшем случае):
struct S
{
int x;
int y;
};
static_assert(offset_of<S, int, &S::x>() == 0, "");
static_assert(offset_of<S, int, &S::y>() == sizeof(int), "");
Затем на-constexpr
Форма проще в использовании:
template <typename T, typename R>
std::size_t offset_of(R T::*M)
{
return reinterpret_cast<std::size_t>(&(((T*)0)->*M));
};
с очевидным недостатком, что это не делается во время компиляции (но проще в использовании):
int main()
{
std::cout << offset_of(&S::x) << std::endl;
std::cout << offset_of(&S::y) << std::endl;
}
То, что я ищу, это синтаксис лайк Затем на-constexpr
разнообразие, но все еще полностью компилируемое; однако я не могу придумать синтаксис для этого. Я также был бы счастлив с offset_of<&S::x>::value
(как и остальные черты типа), но не может понять магию синтаксиса для него.
Следующее должно работать (кредиты идут в ответ на этот вопрос за идею)
#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
в соответствии со стандартом C ++ 11, хотя в настоящее время gcc принимает код (см. отчет об ошибке здесь). Кроме того, я нашел отчет о дефекте 1384 который
говорит о том, чтобы сделать правила менее строгими, так что это может измениться в будущем.
Других решений пока нет …