Templates and T*

Teacher's assignment: "explain T*" · GetOwningPawn<T>() · 7 min

In one breath

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 pinC++ template T
Before it's usedGrey pin, no type yetT, no type yet
What sets the typeThe wire you connectWhat you write in <> (or what the compiler infers from the arguments)
When it's decidedWhen you compile the BlueprintAt compile time, not while the game runs
Wrong typeThe wire won't connectA compile error
ExamplesArray nodes, Select, Make ArrayTArray<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

Say it for the assignment

"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.