offsetof - это то, с чем нужно быть очень осторожным в C ++. Это реликт от C. В наши дни мы должны использовать указатели членов. Тем не менее, я считаю, что указатели на элементы данных перегружены и повреждены - я на самом деле предпочитаю offsetof.
Несмотря на это, offsetof полон неприятных сюрпризов.
Во-первых, для ваших конкретных вопросов, я подозреваю, что реальная проблема заключается в том, что они адаптировались относительно традиционного макроса C (который, как я думал, был задан в стандарте C ++). Они, вероятно, используют reinterpret_cast для "это C ++!" причины (так почему (size_t) приведение?), и char & вместо char *, чтобы попытаться немного упростить выражение.
Приведение к типу char выглядит избыточно в этой форме, но, вероятно, это не так. (size_t) не эквивалентен reinterpret_cast, и если вы попытаетесь привести указатели к другим типам в целые числа, вы столкнетесь с проблемами. Я не думаю, что компилятор даже позволяет это, но, честно говоря, я страдаю от сбоя памяти ATM.
Тот факт, что char является однобайтовым типом, имеет некоторую актуальность в традиционной форме, но это может быть только из-за того, что приведение снова верное. Честно говоря, я помню, что бросил в void *, затем char *.
Кстати, если у вас возникли проблемы с использованием специфических для C ++ вещей, они действительно должны использовать std :: ptrdiff_t для окончательного приведения.
В любом случае, возвращаясь к неприятным сюрпризам ...
VC ++ и GCC, вероятно, не будут использовать этот макрос. IIRC, они имеют встроенный компилятор, в зависимости от опций.
Причина в том, чтобы делать именно то, для чего предназначено offsetof, а не то, что делает макрос, что надежно в C, но не в C ++. Чтобы понять это, подумайте, что произойдет, если ваша структура использует множественное или виртуальное наследование. В макросе, когда вы разыменовываете нулевой указатель, вы пытаетесь получить доступ к указателю виртуальной таблицы, которого нет в нулевом адресе, что означает, что ваше приложение, вероятно, дает сбой.
По этой причине некоторые компиляторы имеют встроенную функцию, которая просто использует указанную структуру структур вместо попытки определить тип времени выполнения. Но стандарт C ++ не предписывает и даже не предлагает этого - он существует только по соображениям совместимости с C. И вам все равно нужно быть осторожным, если вы работаете с иерархиями классов, потому что, как только вы используете множественное или виртуальное наследование, вы не можете предполагать, что макет производного класса соответствует макету базового класса - вы должны убедиться, что смещение действительно для точного типа времени выполнения, а не только для конкретной базы.
Если вы работаете с библиотекой структуры данных, возможно, используете единичное наследование для узлов, но приложения не могут видеть или использовать ваши узлы напрямую, offsetof работает хорошо. Но, строго говоря, даже тогда, есть ошибка. Если ваша структура данных находится в шаблоне, узлы могут иметь поля с типами из параметров шаблона (содержащийся тип данных). Если это не POD, технически ваши структуры тоже не POD. И все стандартные требования для offsetof - это то, что он работает для POD. На практике это будет работать - ваш тип не получил виртуальную таблицу или что-то еще только потому, что у него нет члена POD - но у вас нет никаких гарантий.
Если вы знаете точный тип времени выполнения при разыменовании с использованием смещения поля, вы должны быть в порядке даже при множественном и виртуальном наследовании, но ТОЛЬКО если компилятор обеспечивает внутреннюю реализацию offsetof для получения этого смещения в первую очередь , Мой совет - не делай этого.
Зачем использовать наследование в библиотеке структур данных? Ну как на счет ...
class node_base { ... };
class leaf_node : public node_base { ... };
class branch_node : public node_base { ... };
Поля в node_base автоматически совместно используются (с одинаковой компоновкой) как в листе, так и в ветви, что позволяет избежать распространенной ошибки в C при случайно разных макетах узла.
Кстати - с помощью такого рода вещей можно избежать смещения. Даже если вы используете offsetof для некоторых заданий, у node_base могут быть виртуальные методы и, следовательно, виртуальная таблица, если для разыменования переменных-членов не требуется. Следовательно, node_base может иметь чисто виртуальные методы получения, установки и другие методы. Обычно это именно то, что вы должны сделать. Использование offsetof (или указателей на элементы) является сложным, и его следует использовать только в качестве оптимизации, если вы знаете, что оно вам нужно. Например, если ваша структура данных находится в файле на диске, она вам определенно не нужна - несколько накладных расходов на виртуальные вызовы будут незначительными по сравнению с накладными расходами на доступ к диску, поэтому любые усилия по оптимизации должны сводиться к минимизации доступа к диску.
Хммм - там немного по касательной. Упс.