@@ -27,6 +27,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2727// 2. Recovering the owner pointer.
2828// 3. Ordinary call syntax from a data member.
2929// 4. What breaks when offset zero is violated, and the guard against it.
30+ // 5. Composing several such members through inheritance.
3031
3132#include < gtest/gtest.h>
3233
@@ -225,3 +226,74 @@ TEST(ThisPointerOffsetViolated, GuardConfirmsGoodCase) {
225226}
226227
227228} // namespace section_4
229+
230+ // ---------------------------------------------------------------------------
231+ // 5. Composing several such members through inheritance.
232+ //
233+ // Owner in section 3 has one wrapper data member, greet. Giving it a
234+ // second, shout, the same way needs each in its own small base struct,
235+ // GreetBase and ShoutBase below, combined by having Owner inherit from
236+ // both.
237+ //
238+ // Each wrapper's own this only has to recover its own base's address:
239+ // GreetWrapper is GreetBase's sole member, so it sits at offset zero of
240+ // GreetBase unconditionally, exactly as section 2's Wrapper sits at
241+ // offset zero of Owner. Getting from GreetBase's address to Owner's is
242+ // then a base-to-derived static_cast, which the compiler performs
243+ // correctly regardless of GreetBase's actual offset within Owner.
244+ // [[no_unique_address]] on greet and shout still matters for size, same
245+ // as sections 1 and 4, but no longer for correctness: with flat data
246+ // members, an unhonored [[no_unique_address]] misplaces every member
247+ // after the first (section 4's failure mode); here it only costs Owner
248+ // some otherwise-unnecessary bytes. This is the technique Duck (cited
249+ // above) uses to build its vtable_wrapper, one base per interface member.
250+ // ---------------------------------------------------------------------------
251+ namespace section_5 {
252+
253+ struct GreetWrapper {
254+ std::string operator ()(std::string_view visitor) const ;
255+ };
256+
257+ struct GreetBase {
258+ [[no_unique_address]] GreetWrapper greet;
259+ };
260+
261+ struct ShoutWrapper {
262+ std::string operator ()(std::string_view visitor) const ;
263+ };
264+
265+ struct ShoutBase {
266+ [[no_unique_address]] ShoutWrapper shout;
267+ };
268+
269+ struct Owner : GreetBase, ShoutBase {
270+ std::string name = " World" ;
271+ };
272+
273+ std::string GreetWrapper::operator ()(std::string_view visitor) const {
274+ const auto * base =
275+ static_cast <const GreetBase*>(static_cast <const void *>(this ));
276+ const auto * owner = static_cast <const Owner*>(base);
277+ return " Hello, " + std::string (visitor) + " , from " + owner->name + " !" ;
278+ }
279+
280+ std::string ShoutWrapper::operator ()(std::string_view visitor) const {
281+ const auto * base =
282+ static_cast <const ShoutBase*>(static_cast <const void *>(this ));
283+ const auto * owner = static_cast <const Owner*>(base);
284+ return std::string (visitor) + " , " + owner->name + " SHOUTS HELLO!" ;
285+ }
286+
287+ TEST (ThisPointerInheritance, EachMemberReachesTheSameOwner) {
288+ Owner owner;
289+ owner.name = " Owner" ;
290+
291+ EXPECT_EQ (owner.greet (" Reader" ), " Hello, Reader, from Owner!" );
292+ EXPECT_EQ (owner.shout (" Reader" ), " Reader, Owner SHOUTS HELLO!" );
293+ }
294+
295+ TEST (ThisPointerInheritance, NoExtraStorage) {
296+ EXPECT_EQ (sizeof (Owner), sizeof (std::string));
297+ }
298+
299+ } // namespace section_5
0 commit comments