Search code examples
c++referencederived-class

Reference promotion for a derived class with no data members


I have an interesting problem involving a hierarchy of classes in a library that I maintain. A very simplified view of the situation is as follows:

class Base {
// private data + public interface to said data 
};

class ClassA : public Base {
// Behaviour
};

class ClassB : public Base {
// Behaviour
};

So here I have a class that contains the data privately and has a consistent interface. In reality this is a templated class with lots of different storage models. The two derived classes ClassA and ClassB purely add different implementations of the same behaviour and do not contain any data. It should be within the realms of possibility to convert a reference to an instance of ClassA to one of ClassB without invoking any copies. Of course, one can use

ClassA a;
B& a_bref = *reintepret_cast<B*>(&a);

But this breaks all the rules. My question: Is there a safe way to implement such a conversion operator?


Solution

  • Multiple inheritance

    One way to achieve your goal is through multiple inheritance.

    class A : virtual public Base {
    //...
    };
    
    class B : virtual public Base {
    //...
    };
    
    class AorB : public A, public B {
    //...
    };
    

    With virtual inheritance, you only have one Base instance that is shared between the A and B that make up AorB. This approach means you need to create a class that knows which subtypes you might want to flip through. This may or may not be a problem for you, depending on your use case.

    Strategy

    A more flexible approach may be to treat A and B as strategies, and treat Base as the context. In this method, you would not use inheritance. You can separate the data from the interface, so that A and B can inherit the accessor methods, but reference the data.

    class Base {
        friend class Base_Interface;
        //...
    };
    
    class Base_Interface {
        Base &context_;
        //...
        Base_Interface (Base &context) : context_(context) {}
        template <typename X> operator X () { return context_; }
    };
    
    class A : public Base_Interface {
        //...
        A (Base &context) : Base_Interface(context) {}
    };
    
    class B : public Base_Interface {
        //...
        B (Base &context) : Base_Interface(context) {}
    };
    

    Perhaps lazily, there is a template conversion method to allow users of the Base_Interface to convert to some other class that accepts the Base in its constructor. It works out if Base has no public members, with Base_Interface as its only friend.