$5.2.10/2 - "An expression of integral, enumeration, pointer, or pointer-to-member type can be explicitly converted to its own type; such a cast yields the value of its operand."
This means that pointers 'bs2' and 'bs3' are pointing to the same location
$9.2/16 - "Two standard-layout struct (Clause 9) types are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in declaration order) have layout-compatible types (3.9)."
This means that your class and struct are layout compatible.
$9/6-
A standard-layout class is a class that:
— has no non-static data members of type non-standard-layout class (or array of such types) or reference,
— has no virtual functions (10.3) and no virtual base classes (10.1),
— has the same access control (Clause 11) for all non-static data members,
— has no non-standard-layout base classes,
— either has no non-static data members in the most-derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
— has no base classes of the same type as the first non-static data member.108
Since your class has a virtual destructor, your class and struct are not standard layout classes.
However you have added a 'void *' data member to possibly take care of the 'vptr' (thereby possibly mimicking layout compatibility based on your particular compiler implementation)
In this case reinterpret_cast is used to interpret the class pointer (bs2) as a struct pointer (bs3). By default struct members are public. Since the return value of reinterpret cast points to the same memory (refer quote above) where class members are located, you can modify the struct members (which are the same as the original class members).
This is cheating. This is highly discouraged.! This is most likely going to lead to undefined behavior