С++ Смещение компиляции во внутреннем шаблоне - программирование
Подтвердить что ты не робот

С++ Смещение компиляции во внутреннем шаблоне

Мне нужно использовать 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), "");

Форма non 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 (как и все остальные черты типа), но не могу понять для него синтаксическую магию.

4b9b3361

Ответ 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, который говорит о том, чтобы сделать правила менее строгими, поэтому это может измениться в будущем.