Building on our previous lesson on function templates, we now expand our knowledge to templates for member functions.
As we might expect, these templates can be instantiated to create functions within the scope of a class or a struct. In this lesson, we’ll cover:
The syntax for member function templates is identical to those of regular function templates. We provide a list of template parameters, followed by the body of our template.
Below, we define Attack
as a member function template within the Player
 class:
#include <iostream>
class Monster {/*...*/};
class Player {
public:
template <typename T>
void Attack(T Target) {
std::cout << "\nAttacking " << Target.Name;
}
std::string Name;
};
int main() {
Player PlayerOne{"Anna"};
Player PlayerTwo{"Roderick"};
PlayerOne.Attack<Player>(PlayerTwo);
Monster Goblin{"Bonker"};
PlayerOne.Attack<Monster>(Goblin);
}
Attacking Roderick
Attacking Bonker
Naturally, the functions generated by a member function template have all the same capabilities of a member function. For example, they can access the this
pointer, call other member functions, and access member variables in the context of the current object:
#include <iostream>
class Monster {/*...*/};
class Player {
public:
template <typename T>
void Attack(T Target) {
std::cout << Name << " is attacking "
<< Target.Name << '\n';
}
std::string Name;
};
int main() {/*...*/}
Anna is attacking Roderick
Anna is attacking Bonker
When the compiler can deduce which function needs to be instantiated from the template based on our function arguments, we can remove the template arguments:
#include <iostream>
class Monster {/*...*/};
class Player {/*...*/};
int main() {
Player PlayerOne{"Anna"};
Player PlayerTwo{"Roderick"};
PlayerOne.Attack<Player>(PlayerTwo);
PlayerOne.Attack(PlayerTwo);
Monster Goblin{"Bonker"};
PlayerOne.Attack<Monster>(Goblin);
PlayerOne.Attack(Goblin);
}
Anna is attacking Roderick
Anna is attacking Bonker
Sometimes, we’ll be creating member function templates within a class that itself will be an instance of a template:
template <typename ClassParameter>
class SomeClassTemplate {
template <typename FunctionParameter>
void SomeFunctionTemplate() {
// ...
}
};
In this scenario, our member function template will also have access to the class template arguments.
We’ve updated our previous example to make Player
a class template, rather than an individual class. It accepts a template argument specifying the preferred combat style of the class it creates:
#include <iostream>
enum class Style { Melee, Magic };
class Monster {/*...*/};
template <Style CombatStyle>
class Player {
public:
template <typename T>
void Attack(T Target) {
std::cout << Name << " is attacking "
<< Target.Name;
if (CombatStyle == Style::Melee) {
std::cout << " with a sword\n";
} else {
std::cout << " with magic\n";
}
}
std::string Name;
};
int main() {
Player<Style::Magic> PlayerOne{"Anna"};
Monster Goblin{"Bonker"};
PlayerOne.Attack(Goblin);
Player<Style::Melee> PlayerTwo{"Roderick"};
PlayerTwo.Attack(PlayerOne);
}
Anna is attacking Bonker with magic
Roderick is attacking Anna with a sword
The previous example used template argument deduction to allow us to simply write Attack(Goblin)
instead of Attack<Monster>(Goblin)
. We can specify the template arguments if preferred and, in some scenarios, we will need to.
However, now that Player
is a class template, we can no longer use it in scenarios where a type is expected.
When we have a template, and we need a type, we have to provide the template arguments that will generate that type, such as Player<Style::Magic>
:
#include <iostream>
enum class Style { Melee, Magic };
class Monster {/*...*/};
class Player {/*...*/};
int main() {
Player<Style::Magic> PlayerOne{"Anna"};
Monster Goblin{"Bonker"};
PlayerOne.Attack<Monster>(Goblin);
Player<Style::Melee> PlayerTwo{"Roderick"};
PlayerTwo.Attack<Player<Style::Magic>>(
PlayerOne);
}
Anna is attacking Bonker with magic
Roderick is attacking Anna with a sword
Remember: we can introduce type aliases as needed to make our code easier to understand:
#include <iostream>
enum class Style { Melee, Magic };
class Monster {/*...*/};
class Player {/*...*/};
int main() {
using MeleePlayer = Player<Style::Melee>;
using MagicPlayer = Player<Style::Magic>;
MagicPlayer PlayerOne{"Anna"};
Monster Goblin{"Bonker"};
PlayerOne.Attack<Monster>(Goblin);
MeleePlayer PlayerTwo{"Roderick"};
PlayerTwo.Attack<MagicPlayer>(PlayerOne);
}
Anna is attacking Bonker with magic
Roderick is attacking Anna with a sword
If we want to separate the declaration of a member function template from its implementation, our code could look something like this:
#include <iostream>
class Player {
public:
template <typename T>
void Attack(T Target);
std::string Name;
};
template <typename T>
void Player::Attack(T Target) {
std::cout << "\nAttacking " << Target.Name;
}
Where the template function is part of a template class, our implementation would have two sets of template arguments - one for the class, and one for the function.
Additionally, we need to fully qualify what class we’re providing an implementation for, by including the template arguments. With these two considerations combined, our code would look something like this:
#include <iostream>
enum class Style { Melee, Magic };
class Player {/*...*/};
template <Style CombatStyle>
template <typename T>
void Player<CombatStyle>::Attack(T Target) {
std::cout << Name << " is attacking " << Target.Name;
if (CombatStyle == Style::Melee) {
std::cout << " with a sword\n";
} else {
std::cout << " with magic\n";
}
}
In the previous examples, we assume the declaration and definition are within the same file. If we want to split them across different files - such as a header file an and implementation file - there are some additional problems to overcome.
We introduced these problems, and options for working around them, earlier in the chapter:
In this lesson, we learned about member function templates in C++. The key takeaways are:
Learn how to create and use member function templates in classes and structs, including syntax, instantiation, and advanced techniques
Comprehensive course covering advanced concepts, and how to use them on large-scale projects.