Templates and T*
T is a placeholder type the caller fills in, like a wildcard pin in Blueprint that takes on
the type of whatever you connect. T* is "a pointer to whatever T turns out to be". The compiler
writes a separate copy of the function for each type you use it with.
The code
template <class T> // "T is some class, named by the caller"
T* GetOwningPawn() const // returns a pointer to a T
{
return CastChecked<T>(GetOwner()); // cast my owner to a T
}
Calling it:
AWarriorHeroCharacter* Hero = GetOwningPawn<AWarriorHeroCharacter>();
APawn* AnyPawn = GetOwningPawn<APawn>();
For the first call the compiler writes a version with every T replaced by
AWarriorHeroCharacter: it returns an AWarriorHeroCharacter* and uses
CastChecked<AWarriorHeroCharacter>. You write it once and it works for any type.
The Blueprint wildcard analogy
| Blueprint wildcard pin | C++ template T | |
|---|---|---|
| Before it's used | Grey pin, no type yet | T, no type yet |
| What sets the type | The wire you connect | What you write in <> (or what the compiler infers from the arguments) |
| When it's decided | When you compile the Blueprint | At compile time, not while the game runs |
| Wrong type | The wire won't connect | A compile error |
| Examples | Array nodes, Select, Make Array | TArray<T>, Cast<T>, GetOwningPawn<T> |
Where the analogy stops: a template produces a separate compiled copy for each type you use it with
(that's called instantiation). Also, T alone is the wildcard, and the * is a separate
thing: "pointer to". T could just as well be used as T& or TArray<T>.
Why write it as a template?
Without one, the function would return APawn*, and every caller would cast the result themselves:
// without a template: casting at every call site
AWarriorHeroCharacter* Hero = Cast<AWarriorHeroCharacter>(GetOwningPawn());
// with the template: one call, exact type back
AWarriorHeroCharacter* Hero = GetOwningPawn<AWarriorHeroCharacter>();
Why it gets confused with TObjectPtr
Unreal names its template classes with a T prefix: TArray<>,
TObjectPtr<>, TMap<>, TSubclassOf<>. They're templates too,
which is why they take angle brackets. But the lone T in template <class T> is just a
parameter name. It could be called anything, and the convention is a single letter. You've already seen a longer
one: template<class UserObject, typename CallBackFunc> in
the input lesson.
class and typename mean exactly the same thing inside template < >.
The rest of the line
constafter the brackets: calling it doesn't change the component.CastChecked: crashes on purpose (in development builds) if the owner isn't aT. Asking for the wrong type is a setup bug, so you want it loud.- Body in the header: template bodies must be visible wherever they're used, because the compiler generates each copy at the call site.
- The safety guard the course version usually has:
This makesstatic_assert(TPointerIsConvertibleFromTo<T, APawn>::Value, "'T' Template Parameter to GetPawn must be derived from APawn");GetOwningPawn<UTexture>()a compile error instead of a runtime crash.static_assertis checked by the compiler;checkis checked while the game runs.
"T is a template parameter, a type the caller supplies, so T* is a pointer to whatever type
they asked for. The function casts the owner to that type and returns it, so callers get the exact type back without
casting. It's like a wildcard pin: the type is fixed when you use it, at compile time."
Quiz yourself
In template <class T> T* GetOwningPawn(), what is T?
A template parameter: a placeholder type the caller supplies, e.g. GetOwningPawn<AWarriorHeroCharacter>().
Is the T in template <class T> related to TObjectPtr?
No. It's just a parameter name. Unreal's T-prefixed names (TArray, TObjectPtr) are template classes; the prefix is a naming convention.
When is T decided: at compile time or while the game runs?
Compile time. The compiler generates a separate copy of the function for each type it's used with.
Why make GetOwningPawn a template instead of returning APawn*?
So callers get the exact type back in one call, instead of casting the result at every call site.
How is T like a Blueprint wildcard pin, and where does the analogy stop?
Both take their type from what you connect or supply, and a wrong type won't compile or connect. But a template produces a separate compiled copy per type, and the * is a separate "pointer to", not part of the wildcard.
static_assert vs check?
static_assert is checked by the compiler, so a wrong type won't build. check is checked while the game runs, and is removed in Shipping.
Why must a template function's body be in the header?
The compiler generates each type's version where it's called, so the full body must be visible there.
template <class T> vs template <typename T>?
Identical in this position. Pure style.