Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Infer 'this' pointer type when called from derived class?

I have a method in a baseclass that needs the type passed to it for some type-related operations (lookup, size, and some method invocation). Currently it looks like this:

class base
{
    template<typename T>
    void BindType( T * t ); // do something with the type
};

class derived : public base
{
    void foo() { do_some_work BindType( this ); } 
};

class derivedOther : public base 
{
    void bar() { do_different_work... BindType( this ); } 
};

However, I wonder if there's a way to get the caller's type without having to pass this so that my callpoint code becomes:

class derived : public base
{
  void foo() { BindType(); } 
};

Without the explicit this pointer. I know that I could supply the template parameters explicitly as BindType<derived>(), but is there a way to somehow extract the type of the caller in some other way?

like image 825
Steven Avatar asked Dec 19 '22 12:12

Steven


2 Answers

There's no magical way to get the caller's type, but you can use CRTP (as a comment mentions) in order to automate this behavior, at the cost of a bit of code complexity:

class base
{
    template<typename T>
    void BindType(); // do something with the type
};

template <class T>
class crtper : base
{
     void BindDerived { BindType<T>(); }
}

class derived : public crtper<derived>
{
    void foo() { do_some_work BindDerived(); } 
};

class derivedOther : public crtper<derivedOther>
{
    void bar() { do_different_work... BindDerived(); } 
};

Edit: I should mention, I would kind have expected that foo would be a virtual function, defined without implementation in base. That way you would be able to trigger the action directly from the interface. Although maybe you have that in your real code, but not in your example. In any case, this solution is perfectly compatible with this.

Edit2: After question edit, edited to clarify that solution still applies.

like image 158
Nir Friedman Avatar answered Dec 21 '22 03:12

Nir Friedman


If you want to avoid BindType<derived>(), consider (a bit verbose, I agree) BindType<std::remove_reference<decltype(*this)>::type>(); to avoid passing a parameter. It gets resolved at compile-time and avoids run-time penalties.

class base
{
protected:
    template<typename T>
    void BindType() { cout << typeid(T).name() << endl; } // do something with the type
};

class derived : public base
{
public:
    void foo()
    {
        BindType<std::remove_reference<decltype(*this)>::type>();
    }
};
like image 30
AlexD Avatar answered Dec 21 '22 01:12

AlexD