Search code examples
c++templatestypedefmultiple-inheritance

How to correctly disambiguate inherited typedefs? And how to ease their creation?


Let's consider such code where I have created Typedef class to automatically inherit aliases.

#include <memory>

template <typename T>
struct Typedef
{
    typedef std::unique_ptr<T> Ptr;
    typedef std::unique_ptr<const T> ConstPtr;
};

struct X : public Typedef<X> { Ptr Func(); }; // no problem, Ptr = std::unique_ptr<X>
struct Y : public Typedef<Y> { Ptr Func(); }; // no problem, Ptr = std::unique_ptr<Y>

struct A : public X, public Typedef<A>
{
    using Typedef<A>::Ptr; // (A), better than typedef typename Typedef<A>::Ptr Ptr, am I right?
    Ptr Func();
};

struct B : public X, public Y, public Typedef<B>
{
    using Typedef<B>::Ptr; // (B)
    Ptr Func();
};

Generally there are no problems with this, but when I inherit from multiple classes I have to disambiguate betweem multiple inherited aliases.

  1. Is there any simpler way to disambiguate? (Possibly with C++14/17 features)

  2. If not, is there any simpler way to automatically create aliases for each class? I don't want to typedef std::unique_ptr<Foo> Ptr in each class definition. I want to have Foo::Ptr, Foo::ConstPtr publicly visible in each class.

  3. Is newest Eclipse Oxygen parser broken? It generally understands code correctly (including A), but highlights (B) with error: "typedef is ambiguous". Seems the cause is multiple inheritance in this case. GCC has no problem with it, compiles without any warnings.

  4. If my Typedef class declares more aliases, and I have conflicts - is there any simpler way to resolve all conflicts than using Typedef<Foo>::Ptr, ...::ConstPtr them all?


Solution

  • If you really insist on that behavior, one possibility is to add extra classes...

    struct XBase { ... };
    struct YBase { ... };
    
    struct X : public XBase, public Typedef<X> { ... };
    struct Y : public YBase, public Typedef<Y> { ... };
    
    struct ABase { ... };
    struct BBase { ... };
    
    struct A : 
        public ABase, 
        public XBase, 
        public Typedef<A> { ... };
    
    struct B : 
        public BBase, 
        public XBase, 
        public YBase, 
        public Typedef<B> { ... };
    

    That way, each class has a easy way to provide its own aliases even with multiple inheritance.

    However, as explained in my comment, I do not generally suggest such approach.

    Multiple (public) inheritance should be avoided as it create strong coupling. It is generally better to use composition instead. An exception would be for interface classes (only pure virtual functions and possibly a virtual destructor if you intend to be able to destroy an object from its interface pointer). Mix-in might be another case when multiple inheritance could be useful but in such case, you would use private inheritance and even then composition might be preferable. The less coupling you have, the easier the system will be to maintain if you ever want to do major changes.

    Also as explained in my comments, it might not be a good idea to define such typedefs since std::unique_ptr<> has behavior associated to it and it might be better for documentation purpose that you write std::unique_ptr<A> Func(); anyway. It make it more obvious how the function should be used.