views:

93

answers:

3

Hello,

I'm having issues with multiple inheritance from different instantiations of the same template class. Specifically, I'm trying to do this:

template <class T>
class Base
{

public:

    Base() : obj(NULL)
    {
    }

    virtual ~Base()
    {
        if( obj != NULL ) delete obj;
    }

    template <class T>
    T* createBase()
    {
        obj = new T();

        return obj;
    }

protected:

    T* obj;

};

class Something
{
    // ...
};

class SomethingElse
{
    // ...
};

class Derived : public Base<Something>, public Base<SomethingElse>
{

};

int main()
{
    Derived* d = new Derived();
    Something* smth1 = d->createBase<Something>();
    SomethingElse* smth2 = d->createBase<SomethingElse>();

    delete d;

    return 0;
}

When I try to compile the above code, I get the following errors:

1>[...](41) : error C2440: '=' : cannot convert from 'SomethingElse *' to 'Something *'
1>        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
1>        [...](71) : see reference to function template instantiation 'T *Base<Something>::createBase<SomethingElse>(void)' being compiled
1>        with
1>        [
1>            T=SomethingElse
1>        ]
1>[...](43) : error C2440: 'return' : cannot convert from 'Something *' to 'SomethingElse *'
1>        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

The issue seems to be ambiguity due to member obj being inherited from both Base< Something > and Base< SomethingElse >, and I can work around it by disambiguating my calls to createBase:

Something* smth1 = d->Base<Something>::createBase<Something>();
SomethingElse* smth2 = d->Base<SomethingElse>::createBase<SomethingElse>();

However, this solution is dreadfully impractical, syntactically speaking, and I'd prefer something more elegant. Moreover, I'm puzzled by the first error message. It seems to imply that there is an instantiation createBase< SomethingElse > in Base< Something >, but how is that even possible? Any information or advice regarding this issue would be much appreciated.

+1  A: 

It seems to imply that there is an instantiation createBase< SomethingElse > in Base< Something >, but how is that even possible?

There surely is, because your createBase<T>() is a member template function (and the T in this function has nothing to do with the T in the surrounding class).

I'd do something like:

// in Derived, or you could make some class (eg. MultiBase) for it

template <class T>
T* createBase()
{
  return Base<T>::createBase();
}
jpalecek
A: 

The "full names" of both functions is something like this:

template<class T> T* Derived::Base<Something>::createBase<T>();

From what I know, your createBase() function is a templated function within a templated class. From what I can see, you want to drop the `template`` in front of it.

However, that shouldn't completely solve your problem, because Derived would then (still) have both Base<Something>::createBase() and Base<SomethingElse>::createBase().

jpalecek's answer would finish solving the problem for you, or you can access the object as if it were a particular base:

Base<Something> * pBase = new Derived();
pBase->createBase();

or

static_cast<Base<Something> >(d)->createBase();

Or, to add to jpalecek's answer,

static_cast<Base<T> >(this)->createBase(); 

Should work, and I think be type-safe; that is, won't work if this isn't inheriting from Base<T>

Narfanator
A: 

Thank you for your answers, everyone. I think I'm going to go with jpalecek's solution, as it seems to be the closest to what I'm trying to achieve.

Tom P.