Question
In C++ templates, I have seen both of these declarations:
template <typename T>
template <class T>
What is the difference between them?
Also, what do these keywords mean in the following example?
template <template <typename, typename> class Container, typename Type>
class Example
{
Container<Type, std::allocator<Type>> baz;
};
I want to understand when typename and class mean the same thing, and when they serve different purposes in template syntax.
Short Answer
By the end of this page, you will understand that in a template parameter list, typename and class usually mean the same thing when introducing a type parameter. You will also learn the important exception: typename has another role in C++ for disambiguating dependent names. The nested template example will make sense step by step, including how a template template parameter works.
Concept
In C++, both typename and class can be used to declare a type template parameter.
For example, these are equivalent:
template <typename T>
class Box;
template <class T>
class Box;
In both cases, T stands for a type such as int, std::string, or a user-defined class.
When they mean the same thing
Inside a template parameter list, typename and class are interchangeable for naming a type parameter:
template <typename T>
void func(T value);
template <class T>
void func(T value);
Both say: “this template takes a type parameter named T.”
Why two keywords exist
Historically, class was the original keyword used for type template parameters. Later, typename was added because it is clearer: the parameter can be any type, not just a class.
For example, this is valid:
template <typename T>
T add(T a, T b) {
return a + b;
}
int result = add<int>(2, 3);
int is not a class, but it is still a type.
The important difference
Outside the template parameter list, typename has an additional meaning that class does not have.
It is used to tell the compiler that a dependent name refers to a type.
Example:
template <typename T>
void print_first(const T& container) {
typename T::const_iterator it = container.begin();
}
Here, T::const_iterator depends on T. The compiler cannot know in advance whether const_iterator is a type or something else, so typename is required.
You cannot write:
class T::const_iterator it;
That would be invalid.
So the rule is:
- In a template parameter list,
typenameandclassare usually equivalent. - In other places,
typenamecan be required to mark a dependent type name. classcannot be used for that purpose.
Understanding the example
Consider this template:
template <template <typename, typename> class Container, typename Type>
class Example
{
Container<Type, std::allocator<Type>> baz;
};
This declares a class template named Example with two template parameters:
ContainerType
typename Type
This part is simple:
typename Type
It means Type is a type parameter.
For example:
Example<std::vector, int>
Here, Type becomes int.
template <typename, typename> class Container
This is a template template parameter. That means Container is not a type itself. It is a template that takes two type parameters.
So Container could be something like std::vector in older-style examples, or another container template that matches that shape.
This part:
template <typename, typename> class Container
means:
Containermust be a class template- that class template must accept two type parameters
Then inside Example, it is used like this:
Container<Type, std::allocator<Type>> baz;
So if Container is std::vector and Type is int, then baz becomes:
std::vector<int, std::allocator<int>> baz;
Why this matters
This syntax is useful when you want to write reusable code that works with a family of templates, not just one specific type. It appears in generic libraries, wrappers, allocators, and policy-based designs.
Mental Model
Think of a template like a form with blanks to fill in.
typename Torclass Tmeans: “fill this blank with a type.”template <typename, typename> class Containermeans: “fill this blank with a template that itself needs two type blanks.”
So there are two levels:
- A blank for a normal type, like
int - A blank for a template, like
std::vector
A simple analogy:
typename Tis like asking for a material: wood, steel, plastictemplate <typename, typename> class Containeris like asking for a blueprint that can build something once you provide materials
Then this line:
Container<Type, std::allocator<Type>> baz;
says: use the blueprint Container, fill in the type Type and an allocator, and create an object named baz.
Syntax and Examples
Basic syntax
Type template parameter using typename
template <typename T>
class Box {
public:
T value;
};
Type template parameter using class
template <class T>
class Box {
public:
T value;
};
These mean the same thing.
Example: both forms are equivalent
#include <iostream>
template <typename T>
T square1(T x) {
return x * x;
}
template <class T>
T square2(T x) {
return x * x;
}
int main() {
std::cout << square1<int>(4) << '\n';
std::cout << square2<int>(5) << '\n';
}
Both functions work the same way.
Example where typename is required
#include <vector>
template <typename T>
void showFirst(const T& container) {
typename T::const_iterator it = container.begin();
}
Why?
T::const_iterator depends on T. The compiler needs help to know it is a type.
Template template parameter example
#include <vector>
#include <memory>
template <template <typename, typename> class Container, typename Type>
class Example {
public:
Container<Type, std::allocator<Type>> baz;
};
int main() {
Example<std::vector, int> ex;
ex.baz.push_back(42);
}
Explanation
Containermust be a template taking two type argumentsTypeis a regular type parameterbazbecomesstd::vector<int, std::allocator<int>>
Modern style note
Many C++ developers prefer typename for type template parameters because it reads more clearly. But class is still correct and common.
Step by Step Execution
Consider this example:
#include <vector>
#include <memory>
template <template <typename, typename> class Container, typename Type>
class Example {
public:
Container<Type, std::allocator<Type>> baz;
};
int main() {
Example<std::vector, int> ex;
ex.baz.push_back(10);
ex.baz.push_back(20);
}
Step-by-step
1. The template is declared
template <template <typename, typename> class Container, typename Type>
This says Example needs:
Container: a class template that takes two type parametersType: a normal type
2. main() creates an instance
Example<std::vector, int> ex;
Now substitute:
Container = std::vectorType = int
3. The member declaration is expanded
Inside the class, this:
Container<Type, std::allocator<Type>> baz;
becomes:
std::vector<int, std::allocator<int>> baz;
4. The object is constructed
ex now contains a vector of integers named baz.
5. Values are added
ex.baz.push_back(10);
ex.baz.push_back(20);
The vector stores 10 and 20.
Key takeaway
The outer template Example accepts another template (std::vector) as an argument, then uses it to build a concrete type.
Real World Use Cases
Generic containers and wrappers
You may want a class to work with different container templates:
std::vectorstd::list- custom container templates
Reusable library components
Library code often accepts types and templates as parameters so the user can customize behavior without rewriting logic.
Allocator-aware code
The example uses std::allocator<Type>, which is relevant in codebases that care about memory allocation strategy.
Policy-based design
A class may accept templates that define behavior, storage, or configuration.
Generic algorithms
Understanding typename is essential when writing algorithms that depend on nested types such as:
T::value_typeT::iteratorT::const_iterator
Example:
template <typename Container>
typename Container::value_type lastValue(const Container& c) {
return c.back();
}
Here, typename is needed because Container::value_type is a dependent type.
Real Codebase Usage
In real C++ projects, developers use these ideas in a few common ways.
1. Generic type parameters
This is the most common usage:
template <typename T>
class Result {
T value;
};
This appears in utility classes, containers, wrappers, and algorithms.
2. Dependent nested types
This is extremely common in generic code:
template <typename Container>
void process(const Container& c) {
typename Container::const_iterator it = c.begin();
(void)it;
}
3. Validation through template shape
Template template parameters let APIs require a specific template form:
template <template <typename, typename> class Seq, typename T>
class Buffer {
Seq<T, std::allocator<T>> data;
};
4. Early constraints by interface shape
Even before modern concepts, developers used template signatures to restrict what kinds of templates were accepted.
5. Readability conventions
Teams often choose one style consistently:
- use
typenamefor type parameters - use
classonly when defining actual classes
This is a style choice, not a rule.
6. Error handling in templates
Template code often produces confusing compiler errors. Clear naming helps:
template <typename ValueType>
class Storage;
Using descriptive names can make template-heavy code easier to read and debug.
Common Mistakes
Mistake 1: Thinking class means only user-defined classes
Wrong idea:
template <class T>
T add(T a, T b);
Some beginners think T must be a class type. That is false.
This works with built-in types too:
add<int>(1, 2);
Mistake 2: Forgetting typename for dependent types
Broken code:
template <typename T>
void f() {
T::value_type x;
}
Fix:
template <typename T>
void f() {
typename T::value_type x;
}
Why: the compiler needs to know T::value_type is a type.
Mistake 3: Trying to use class where only typename works
Broken code:
template <typename T>
void f() {
class T::value_type x;
}
This is invalid C++.
Mistake 4: Misreading template template parameters
Broken assumption:
template <template <typename, typename> class Container, typename Type>
class Example;
This does not mean Container is an object or a regular type. It is itself a template.
Mistake 5: Passing a template with the wrong parameter shape
If the template parameter expects two type parameters, the supplied template must match.
Example shape expected:
template <typename, typename>
class Something;
If you pass a template with a different signature, compilation can fail.
Mistake 6: Assuming all containers match the same template form
Not every container template has exactly the same template parameters or defaults in every context. Always check the actual template declaration.
Comparisons
| Concept | Meaning | Where used | Notes |
|---|---|---|---|
typename T | T is a type parameter | Template parameter lists | Equivalent to class T here |
class T | T is a type parameter | Template parameter lists | Historical syntax; still valid |
typename T::value_type | T::value_type is a type | Inside template definitions | typename may be required |
class T::value_type | Invalid in this context | Not used | class cannot replace typename here |
typename vs class in template parameter lists
| Form | Valid? | Meaning |
|---|---|---|
template <typename T> | Yes | Type parameter |
template <class T> | Yes | Type parameter |
typename in other contexts
| Form | Valid? | Why |
|---|---|---|
typename T::iterator it; | Yes | Marks dependent type |
T::iterator it; | Often no | Compiler may not know it is a type |
class T::iterator it; | No | Wrong keyword |
Type parameter vs template template parameter
| Syntax | What it accepts | Example |
|---|---|---|
typename T | A type | int, std::string |
template <typename, typename> class C | A class template with two type parameters | std::vector in this example style |
Cheat Sheet
Quick rules
- In a template parameter list,
typenameandclassusually mean the same thing. - Both can declare a type parameter.
typenameis also used elsewhere to mark a dependent name as a type.classcannot be used for that second purpose.
Equivalent forms
template <typename T>
class A;
template <class T>
class A;
When typename is required
template <typename T>
void f() {
typename T::value_type x;
}
Template template parameter syntax
template <template <typename, typename> class Container, typename T>
class Example {
Container<T, std::allocator<T>> data;
};
Read this as
typename T→Tis a typeclass T→Tis a typetemplate <...> class Container→Containeris a template
Common style guideline
Many developers prefer:
typenamefor template type parametersclassfor actual class declarations
But this is style, not a language rule.
Key exception to remember
This is valid:
typename T::iterator it;
This is not:
class T::iterator it;
FAQ
Is there any difference between typename and class in template <...>?
In a template parameter list for a type parameter, no. They mean the same thing.
Should I prefer typename or class in C++ templates?
Many developers prefer typename because it is clearer that the parameter can be any type, not just a class. But both are correct.
Why does typename sometimes appear before T::value_type?
Because T::value_type is a dependent name. The compiler needs typename to know it refers to a type.
Can I use class instead of typename before T::value_type?
No. That use is specific to typename.
What is a template template parameter?
It is a template parameter that accepts another template as an argument.
What does template <template <typename, typename> class Container> mean?
It means Container must be a class template that takes two type parameters.
Does template <class T> mean T must be a class type?
No. T can be any type, including int, double, pointers, or user-defined types.
Why is std::allocator<Type> used in the example?
Because the Container template is expected to take two type arguments, and many standard containers use the second one as an allocator type.
Mini Project
Description
Build a small generic wrapper class that can store values inside a container template such as std::vector. This project demonstrates two key ideas from the question: regular type template parameters and template template parameters. It also helps you read and write nested template syntax with more confidence.
Goal
Create a reusable class that accepts a container template and a value type, stores values, and prints the number of stored items.
Requirements
- Create a class template that accepts a container template and a type parameter.
- Store data using the provided container with
std::allocator. - Add a method to insert values into the container.
- Add a method to return the number of stored elements.
- Instantiate the class with
std::vectorandintinmain().
Keep learning
Related questions
Advantages of Brace Initialization in C++
Learn why C++ brace initialization is often clearer and safer than other object initialization styles, with examples and common pitfalls.
Basic Rules and Idioms for Operator Overloading in C++
Learn the core rules, syntax, and common idioms for operator overloading in C++, including member vs non-member operators.
C++ Aggregates, Trivial Types, Trivially Copyable Types, and PODs Explained
Learn what aggregates, trivial types, trivially copyable types, and PODs mean in C++, how they differ, and why they matter.