Jaa-c
Jaa-c

Reputation: 5137

Call inherited method inside template function

I have a template method in a parent class, that should call another method from the base class. It works, if the method is explicitly defined in the base class, but it doesn't work if the method is inherited. I can't figure out what's exactly wrong with this code (although I know it's a little weird :)

class A
{
protected:
    virtual void someMethod() 
    {
    }

    template <class TBaseClass>
    void templateMethod()
    {
        TBaseClass::someMethod();
    }

};

class B : public A 
{   

};

class C : public B
{

protected:
    virtual void someMethod()
    {
        templateMethod<A>(); // this works
        templateMethod<B>(); // this doesn't
    }

};

This ends up with compiler error:

error C2352: 'A::someMethod' : illegal call of non-static member function

What exactly is wrong? I'm not looking for workarounds, that's easy. I'd like to know why is this incorrect.

Upvotes: 1

Views: 571

Answers (3)

TruckerCat
TruckerCat

Reputation: 1487

I think it's not working because you are trying to call a static method. Static methods can be inherited but they can't be virtual. Thats why A::someMethod will work and B::someMethod won"t work.

Upvotes: 0

Jan Hudec
Jan Hudec

Reputation: 76246

In template <TBaseClass> void A::templateMethod() the invocant, this, is of type A *. So when you try to call B::someMethod on it, the compiler won't recognize it a object method call, because B is not a base class, but it can still be a static method call, so the compiler will try that, find B::someMethod inherited via A and complain it is not static. The fact that A is a base class of this is not relevant; only that B is not.

Upvotes: 1

Jay Miller
Jay Miller

Reputation: 2234

The issue is that class A is not automatically granted access to class B. The methods within the class don't know that the current instance of A is actually of type B.

To do what you're trying, you need to cast A to the target type:

template <class TBaseClass>
void templateMethod()
{
    static_cast<TBaseClass*>(this)->someMethod();
}

This will work if the particular instance of A you're calling really is a B (as it is in the example you give). There is no type checking done here to be sure it is. If you pass a class that is not in the inheritance hierarchy you will be in the work of undefined behavior.

That said, your example has "someMethod()" as virtual. If you're doing what I just did here then making that method virtual may not make sense, as you are explicitly saying which instance you want. If you do leave it virtual, then just calling someMethod() directly in A will get you the most derived instance.

Upvotes: 0

Related Questions