Использование 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;
}

2 ответа

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

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

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

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

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

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

Другие вопросы по тегам