我的代码的简化版本如下:
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
中包含一些允许自动对其模板类进行类型转换的事情似乎是合理的({{1 }})。
我追求的是T
吗?有人可以提供代码示例来完成这项工作吗? This Question建议我可以这样做:
演员表总是在operator=
和this
之间。
T*
但错误仍然存在。
答案 0 :(得分:3)
虽然在我看来有些愚蠢,但如果我删除这一行[...]
,它就不会抱怨
不,这不是愚蠢的,而是模板的工作方式。如果您从不调用类模板的成员函数将永远不会被实例化。因此,在实例化它们时将产生的编译错误将不会显示。
问题可以通过明确的类型转换来解决,如[...]
我更喜欢static_cast<>
:
SayHello( static_cast<T*>(this) );
this
函数收到的SaySomething()
指针的类型为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); }
};
答案 1 :(得分:3)
您可以添加一个帮助函数,将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); }
隐式转换削弱了类型系统并且可能成为错误的来源,我认为derived_this()
这样的显式函数更清晰,更安全。