Reputation: 23586
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.
Upvotes: 3
Views: 1393
Reputation: 171253
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.
Upvotes: 3
Reputation: 126412
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); }
};
Upvotes: 3