Использование offsetof для шаблонных классов

Из стандарта C ++:

Класс стандартного макета — это класс, который:

— не имеет нестатических членов данных типа нестандартного класса макета (или
массив таких типов) или ссылка,

— не имеет виртуальных функций (10.3) и виртуальных базовых классов (10.1),

— имеет одинаковый контроль доступа (пункт 11) для всех нестатических элементов данных,
— не имеет нестандартных макетов базовых классов,

— либо не имеет нестатических членов данных в наиболее производном классе и
не более одного базового класса с нестатическими членами данных или не имеет базы
классы с нестатическими членами данных, и

— не имеет базовых классов того же типа, что и первые нестатические данные
член

Макрос offsetof (тип, член-указатель) принимает ограниченный набор
аргументов типа в этом международном стандарте. Если тип не является
класс стандартного макета (раздел 9), результаты не определены

Учитывая эти заявления, есть ли безопасный способ использования offsetof для членов, которые зависят от параметров шаблона? Если нет, как я могу получить смещение члена в шаблонных классах? Что может быть небезопасно при использовании чего-то вроде:

//MS Visual Studio 2013 definition
#define offsetof(s,m)   (size_t)&reinterpret_cast<const volatile char&>((((s *)0)->m))

на нестандартных классах макета?

После образца, где не безопасно в соответствии со стандартом:

#include <cstddef>
#include <iostream>

template<typename T>
struct Test
{
int     a;
T       b;
};

struct NonStdLayout
{
virtual void f(){};
};

int main()
{
std::cout << offsetof(Test<int>, b) << std::endl;
std::cout << offsetof(Test<NonStdLayout>, b) << std::endl;
return 0;
}

3

Решение

offsetof не может использоваться в классах нестандартной компоновки просто потому, что их компоновка в памяти неизвестна. Например, стандарт не определяет, как реализованы функции виртуального члена. Один из распространенных способов сделать это — добавить указатель на vtable в качестве первого члена данных класса, но это не единственный способ.

Что касается вашего определения offsetof: нет гарантии, что нулевой указатель преобразуется в 0 с помощью reinterpret_cast (или через приведение в стиле C), также нет никакой семантики, определенной для других значений указателей, приведенных к целым числам.

Так если вы знаете, что ваше определение имеет смысл в базовой схеме адресации, используемой вашим компилятором для вашей платформы, она может работать. Но это если Вы должны быть в курсе.

1

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

Ответ в том, что в шаблонах совершенно безопасно использовать offsetof. При этом никакого вреда не будет. Однако, если вы решите сделать это, вы наложите ограничение на тип параметра для шаблона. Он будет работать правильно для стандартных классов компоновки, и в принципе, по крайней мере, компилятор должен сообщать вам, когда параметр имеет тип, для которого он не будет работать.

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

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

1

По вопросам рекламы [email protected]