The macro, inherited from C and applicable to standard-layout
classes (and, conditionally, other classes) in C++, calculates the layout
offset of a member within a class. is useful for calculating an
object pointer given a pointer to one of its members:
struct link { ... }; struct container { link l ; }; container * container_from_link ( link * x ) { // x is known to be the .l part of some container uintptr_t x_address = reinterpret_cast < uintptr_t > ( x ); size_t l_offset = offsetof ( container , l ); return reinterpret_cast < container *> ( x_address - l_offset ); }
This pattern is used in several implementations of intrusive containers, such
as Linux kernel linked lists ().
Unfortunately, although works for some unusual
member-designators, it does not work for pointers to members. This won’t
compile:
template < typename Container , typename Link , Link ( Container ::* member ) > Container * generic_container_from_link ( Link * x ) { uintptr_t x_address = reinterpret_cast < uintptr_t > ( x ); size_t link_offset = offsetof ( Container , member ); // error! return reinterpret_cast < Container *> ( x_address - link_offset ); }
Programmers currently compute pointer-to-member offsets using casts
(i.e., the incorrect folk implementation of , which invokes
undefined behavior), or by jumping through other hoops:
template < typename Container , typename Link , Link ( Container ::* member ) > Container * generic_container_from_link ( Link * x ) { ... alignas ( Container ) char container_space [ sizeof ( Container )] = {}; Container * fake_container = reinterpret_cast < Container *> ( container_space ); size_t link_offset = reinterpret_cast < uintptr_t > ( & ( fake_container ->* member )) - reinterpret_cast < uintptr_t > ( fake_container ); ... }
with pointer-to-member member-designators should simply work.
Modern compilers implement using an extension ( in GCC and LLVM), so implementation need not require library changes. To avoid
ambiguity, we propose this syntax:
size_t link_offset = offsetof ( Container , . * member );
1. Questions
Must a pointer-to-member expression in an member-designator be a
constant expression (such as a template argument)? The C standard requires
that “the expression evaluates to an address
constant,” which might make this code illegal:
struct container { char array [ 200 ]; }; int index = /* dynamic value */ ; size_t offset = offsetof ( container , array [ index ]); // questionable
But since several current compilers accept dynamic array indexes, the proposed wording allows any pointer to member.
2. Proposed Wording
In Sizes, alignments, and offsets [support.types.layout], modify the first sentence of ❡1 as follows:
The macro
has the same semantics as the corresponding macro in the C standard library headeroffsetof ( type , member - designator ) , but accepts a restricted set of< stddef . h > arguments and a superset oftype arguments in this International Standard.member - designator
Add this paragraph after ❡1:
Anoffsetof may contain pointer-to-member expressions as well asmember - designator acceptable in C. Amember - designators may begin with a prefixmember - designator or. operator (e.g.,. * oroffsetof ( type , . member_name ) ). If the prefix operator is omitted,offsetof ( type , . * pointer_to_member ) is assumed..