Typecasting `this` of a base class template to its derived class

浪尽此生 提交于 2019-12-23 18:01:37

问题


A simplified version of my code looks like so:

template <class T>
struct Base
{
    void SayHello( T* aDerived )
    {
    }

    void SaySomething()
    {
        SayHello( this ); // This is where the error happens
    }
};

struct Derived : public Base< Derived >
{
};

int main(int argc, const char * argv[])
{
    Derived iDerived;
    iDerived.SaySomething();
}

And it won't compile on the SayHello( this ) line with this error message:

Cannot initialize a parameter of type 'Derived *'
with an rvalue of type 'Base<Derived> *'

Now it makes sense for the compiler to complain about this, although it seems to me somewhat stupid it doesn't complain if I remove this line:

iDerived.SaySomething();

Anyhow, the problem can be solved by doing an explicit typecast, like so:

SayHello( (T*)this );

The thing is that my actual code ends up with many of these typecasts, and it seems to me reasonable to just include in Base something that will allow it to be typecasted automatically to its template class (T).

Is it the operator= I'm after? Can someone provide a code sample to how this is done? This Question suggests I can do something like:

The cast is always between this and T*.

operator T*()
{
    return (T*)this;
}

But the error remains.


回答1:


You could add a helper function that returns this downcast to the derived type

template <class T>
struct Base
{
    void SayHello( T* aDerived )
    {
    }

    void SaySomething()
    {
        SayHello( derived_this() );
    }

private:
    T* derived_this()
    {
        return static_cast<T*>(this);
    }

You might also want a const overload:

    const T* derived_this() const
    {
        return static_cast<const T*>(this);
    }

You could add an implicit conversion operator, but I would not recommend it:

    operator T*() { return static_cast<T*>(this); }

Implicit conversions weaken the type system and can be a source of bugs, I think an explicit function such as derived_this() is clearer and safer.




回答2:


Although it seems to me somewhat stupid it doesn't complain if I remove this line [...]

No, that's not stupid, it's how templates work. A member function of a class template will never be instantiated if you never call it. Consequently, compilation errors which would be generated while instantiating them won't show up.

The problem can be solved by doing an explicit typecast, like so [...]

I would prefer a static_cast<>:

SayHello( static_cast<T*>(this) );

The this pointer received by your SaySomething() function is of type Base<Derived>, but you know (by design) that the object pointed to is actually of type Derived. Therefore, it is safe to perform a static cast.

it seems to me reasonable to just include in Base something that will allow it to be typecasted automatically to its template class (T).

There's nothing wrong with casting the pointer many times in this case. That's what the CRTP (the design pattern you are using) forces you to do. If you are bothered by it, just define a get_this() function that does the cast for you:

template <class T>
struct Base
{
    void SayHello( T* aDerived )
    {
    }

    void SaySomething()
    {
        SayHello( get_this() );
    }

private:

    // Version returning a non-const pointer
    T* get_this() { return static_cast<T*>(this); }

    // Version returning a const pointer
    T const* get_this() const { return static_cast<T const*>(this); }

};


来源:https://stackoverflow.com/questions/15213391/typecasting-this-of-a-base-class-template-to-its-derived-class

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!