TITLE: simulating multiple inheritance (Newsgroups: comp.lang.c++.moderated, 20 Jul 98) AUTHOR: herbs@cntc.com (Herb Sutter) .--------------------------------------------------------------------. | Guru of the Week problems and solutions are posted regularly on | | news:comp.lang.c++.moderated. For past problems and solutions | | see the GotW archive at http://www.cntc.com. | | Is there a topic you'd like to see covered? mailto:herbs@cntc.com | `--------------------------------------------------------------------' _______________________________________________________ GotW #38: Multiple Inheritance - Part II Difficulty: 8 / 10 _______________________________________________________ >Consider the following example: > > struct A { virtual ~A() { } > virtual string Name() { return "A"; } }; > struct B1 : virtual A { string Name() { return "B1"; } }; > struct B2 : virtual A { string Name() { return "B2"; } }; > > struct D : B1, B2 { string Name() { return "D"; } }; > >Demonstrate the best way you can find to "work around" >not using multiple inheritance by writing an equivalent >(or as near as possible) class D without using MI. How >would you get the same effect and usability for D with >as little change as possible to syntax in the client >code? There are a few strategies, each with their weaknesses, but here's one that gets quite close: struct D : B1 { struct D2 : B2 { void Set ( D* d ) { d_ = d; } string Name(); D* d_; } d2_; D() { d2_.Set( this ); } D( const D& other ) : B1( other ), d2_( other.d2_ ) { d2_.Set( this ); } D& operator=( const D& other ) { B1::operator=( other ); d2_ = other.d2_; return *this; } operator B2&() { return d2_; } B2& AsB2() { return d2_; } string Name() { return "D"; } }; string D::D2::Name() { return d_->Name(); } Some drawbacks are that: - providing operator B2& arguably gives references special (inconsistent) treatment over pointers - you need to call D::AsB2() explicitly to use a D as a B2 (in the test harness, this means changing "B2* pb2 = &d;" to "B2* pb2 = &d.AsB2();") - dynamic_cast from D* to B2* still doesn't work (it's possible to work around this if you're willing to use the preprocessor to redefine dynamic_cast calls) Interestingly, you may have observed that the D object layout in memory is probably identical to what multiple inheritance would give. That's because we're trying to simulate MI, just without all of the syntactic sugar and convenience that builtin language support would provide.