WTL for MFC Programmers, Part I - ATL GUI Classes
ATL-style templates
Even if you can read C++ templates without getting a headache, there are two things ATL does that might trip you up at first. Take this class for example:
class CMyWnd : public CWindowImpl<CMyWnd>{
…
};
That actually is legal, because the C++ spec says that immediately after the class CMyWnd
part, the name CMyWnd
is defined and can be used in the inheritance list. The reason for having the class name as a template parameter is so ATL can do the second tricky thing, compile-time virtual function calls.
To see this in action, look at this set of classes:

class B1
{
public:
void SayHi()
{
T pT = static_cast<T>(this); // HUH?? I’ll explain this below
pT->PrintClassName();
}
void PrintClassName() { cout << "This is B1"; }
};
class D1 : public B1<D1>
{
// No overridden functions at all
};
class D2 : public B1<D2>
{
void PrintClassName() { cout << "This is D2"; }
};
int main()
{
D1 d1;
D2 d2;
d1.SayHi(); // prints "This is B1"
d2.SayHi(); // prints "This is D2"
return 0;
}
The static_cast<T>(this)
is the trick here. It casts this
, which is of type B1
, to either D1
or D2
depending on which specialization is being invoked. Because template code is generated at compile-time, this cast is guaranteed to be safe, as long as the inheritance list is written correctly. (If you wrote class D3 : public B1<D2>
you’d be in trouble.) It’s safe because the this
object can only be of type D1
or D2
(as appropriate), and nothing else. Notice that this is almost exactly like normal C++ polymorphism, except that the SayHi()
method isn’t virtual.
To explain how this works, let’s look at each call to SayHi()
. In the first call, the specialization B1<D1>
is being used, so the SayHi()
code expands to:
{
D1 pT = static_cast<D1>(this);
pT->PrintClassName();
}
Since D1
does not override PrintClassName()
, D1
‘s base classes are searched. B1
has a PrintClassName()
method, so that is the one called.
Now, take the second call to SayHi()
. This time, it’s using the specialization B1<D2>
, and SayHi()
expands to:
{
D2 pT = static_cast<D2>(this);
pT->PrintClassName();
}
This time, D2
does contain a PrintClassName()
method, so that is the one that gets called.
The benefits of this technique are:
- It doesn’t require using pointers to objects.
- It saves memory because there is no need for vtbls.
- It’s impossible to call a virtual function through a null pointer at runtime because of an uninitialized vtbl.
- All function calls are resolved at compile time, so they can be optimized.