将基类模板的“this”类型转换为其派生类

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

本文关键字:类型转换 派生 this 基类      更新时间:2023-10-16

我的代码的简化版本如下:

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();
}

它不会在SayHello( this )行上编译,并显示以下错误信息:

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

现在编译器抱怨这个是有意义的,尽管在我看来有点愚蠢,如果我删除这一行,它不会抱怨:

iDerived.SaySomething();
无论如何,这个问题可以通过显式类型转换来解决,如下所示:
SayHello( (T*)this );

问题是我的实际代码最终会有许多这样的类型转换,在我看来,在Base中包含一些允许它自动类型转换到它的模板类(T)的东西是合理的。

我要的是operator=吗?有人可以提供一个代码样本,这是如何做到的?这个问题建议我可以这样做:

强制转换总是在thisT*之间。

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

您可以添加一个辅助函数,将this向下转换为派生类型

template <class T>
struct Base
{
    void SayHello( T* aDerived )
    {
    }
    void SaySomething()
    {
        SayHello( derived_this() );
    }
private:
    T* derived_this()
    {
        return static_cast<T*>(this);
    }

您可能还需要const重载:

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

可以添加隐式转换操作符,但我不建议这样做:

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

隐式转换削弱了类型系统,并可能成为bug的来源,我认为像derived_this()这样的显式函数更清晰、更安全。

虽然在我看来有些愚蠢,但它不会抱怨如果我删除这一行[…]

不,这并不愚蠢,这就是模板的工作原理。如果不调用类模板的成员函数,它将永远不会被实例化。因此,在实例化它们时产生的编译错误将不会显示。

这个问题可以通过显式的类型转换来解决,像这样[…]

我更喜欢static_cast<>:

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

SaySomething()函数接收到的this指针是Base<Derived>类型的,但是你知道(通过设计)指向的对象实际上是Derived类型的。因此,执行静态强制转换是安全的。

在我看来,在Base中包含一些允许它自动被类型转换到它的模板类(T)的东西是合理的。

在这种情况下,多次强制转换指针没有问题。这就是CRTP(您正在使用的设计模式)强迫您做的事情。如果您对此感到困扰,只需定义一个get_this()函数来为您执行强制转换:
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); }
};