Question
When declaring a function template or class template in C++, both of these forms are valid:
template <class T>
// ...
template <typename T>
// ...
Is there a good reason to prefer class over typename, or vice versa? Are there situations where the two keywords behave differently?
Short Answer
In a normal C++ type template parameter, class and typename mean exactly the same thing. Either keyword can introduce a parameter that will represent a type. The choice is usually a matter of team style and readability. However, typename has another important use in C++: telling the compiler that a dependent qualified name is a type.
Concept
A template lets you write code that works with many types. For example, a Box<T> can store an int, a std::string, or a custom type.
When declaring a type template parameter, C++ accepts either of these declarations:
template <class T>
class Box;
// Equivalent:
template <typename T>
class Box;
In this position, class does not mean that T must be a class. T may be any type, including fundamental types such as int, pointer types, enums, or class types.
template <class T>
class Box {
public:
T value;
};
Box<int> number{42};
Box<double> price{19.99};
So, for ordinary type parameters, neither keyword provides extra capability. Consistency within a project is usually the best reason to choose one.
typename matters elsewhere in templates. When a type depends on another template parameter, the compiler may need typename to know that a qualified name refers to a type rather than a static value or member. That use is separate from declaring a template parameter.
Mental Model
Think of a template parameter as a blank label on a box:
template <class T>
and
template <typename T>
both mean: “Create a blank label named T; later, someone will fill it with a type.” The two labels do the same job.
The word typename has a second job in template code. Imagine the compiler sees T::item. Until it knows what T is, it cannot tell whether item is a type or a value. Writing typename T::item is like attaching a note that says: “Treat item as a type.”
Syntax and Examples
A type parameter can be declared with either keyword.
#include <iostream>
// `class` and `typename` are equivalent here.
template <class T>
T larger(T left, T right) {
return left > right ? left : right;
}
template <typename T>
class Holder {
public:
explicit Holder(T initialValue) : value(initialValue) {}
T get() const {
return value;
}
private:
T value;
};
int main() {
std::cout << larger(4, 9) << '\n';
Holder<double> temperature(21.5);
std::cout << temperature.get() << '\n';
}
larger uses class T, while Holder uses typename T. Both T parameters stand for a type.
A distinct use of typename appears with dependent names:
template <typename Container>
void printFirst(Container container) {
typename Container::value_type first = *container.begin();
// ^^^^^^^^^^^^^^^^^^^^^ This tells the compiler it is a type.
}
Here, Container::value_type depends on the type supplied as Container. The typename keyword disambiguates it as a type.
Step by Step Execution
Consider this function template:
template <typename T>
T add(T left, T right) {
return left + right;
}
int result = add(3, 4);
Execution and compilation happen as follows:
-
template <typename T>declaresTas a placeholder for a type. -
The call
add(3, 4)supplies twointarguments. -
The compiler deduces that
Tisint. -
It produces an
intversion of the function conceptually equivalent to:int add(int left, int right) { return left + right; } -
The function returns
7, which is stored inresult.
If the declaration used template <class T> instead, every step and result would be identical.
Real World Use Cases
Type template parameters are common throughout C++ code:
- Containers:
std::vector<T>,std::optional<T>, andstd::unique_ptr<T>need a type to store or manage. - Generic algorithms: A function can work with different number types, iterators, or user-defined objects.
- Serialization: A template can save and load objects of multiple supported types.
- Library wrappers: A
Result<T>orCache<T>type can expose the same interface for many value types. - Traits and aliases: Template code often obtains a nested type such as
Traits<T>::value_type, where dependent-nametypenamemay be required.
For example, a generic API response wrapper may use either parameter keyword:
template <typename T>
struct ApiResponse {
int statusCode;
T data;
};
ApiResponse<std::string> response{200, "ok"};
Real Codebase Usage
In production code, the choice between class and typename in a type parameter is commonly governed by a style guide.
Common patterns include:
- Use
typenamefor all type parameters because it makes the intent explicit: the parameter represents a type. - Use
classfor type parameters because it is shorter and traditional in generic C++ code. - Use
classin template declarations but reservetypenamefor dependent qualified types, making the special disambiguation easier to notice.
For example, this generic validation function uses an early return and a dependent nested type:
#include <stdexcept>
template <class Container>
typename Container::value_type firstOrThrow(const Container& items) {
if (items.empty()) {
throw std::runtime_error("Container is empty");
}
return *items.begin();
}
class Container declares a type parameter. typename Container::value_type has the different job of identifying a dependent nested type.
Prefer a project-wide convention over mixing keywords randomly. Consistency reduces small distractions during code review.
Common Mistakes
Thinking class T accepts only classes
This is incorrect. It accepts any type.
template <class T>
T identity(T value) {
return value;
}
int count = identity(10); // Valid: T is int
Expecting a behavioral difference in ordinary type parameters
These declarations are equivalent:
template <class T> void process(T value);
template <typename T> void process(T value);
Do not choose one expecting different overload resolution, generated code, or performance.
Forgetting typename for a dependent nested type
The following is typically ill-formed because the compiler does not know whether T::value_type is a type:
template <class T>
void broken() {
T::value_type value; // Error: missing `typename`
}
Write this instead:
template <class T>
void correct() {
typename T::value_type value{};
}
Adding typename where the name is not dependent
Do not add it indiscriminately:
std::vector<int>::value_type count = 0;
std::vector<int>::value_type is already known by the compiler, so no typename is needed.
Confusing type parameters with non-type parameters
A type parameter represents a type:
template <typename T>
struct Box {};
A non-type parameter represents a compile-time value:
template <std::size_t Size>
struct Buffer {};
typename and class only apply to type parameters.
Comparisons
| Situation | class | typename | Notes |
|---|---|---|---|
| Ordinary type template parameter | Yes | Yes | Equivalent. |
| Function template type parameter | Yes | Yes | Equivalent. |
| Class template type parameter | Yes | Yes | Equivalent. |
Dependent qualified type, such as T::value_type | No | Yes | typename may be required to identify it as a type. |
Non-type template parameter, such as int N | No | No | Use the parameter's value type instead. |
A related distinction is between a type parameter and a template template parameter:
// T is a type.
template <typename T>
struct Wrapper {};
// Container is itself a class template.
template <template <class> class Container, class T>
struct UsesContainer {
Container<T> values;
};
Before C++17, class rather than typename was required in the inner parameter list of a template template parameter. In C++17 and later, typename is also permitted there. This historical rule is a language-version compatibility detail, not a preference for ordinary T parameters.
Cheat Sheet
// Equivalent type parameter declarations
template <class T>
struct A {};
template <typename T>
struct B {};
- In a type template parameter,
classandtypenamehave the same meaning. class Tdoes not restrictTto class types.- Choose one based on your codebase's style guide.
typenamealso disambiguates a dependent qualified type:
template <class T>
using Value = typename T::value_type;
- Do not use either keyword for a non-type parameter:
template <int N>
struct FixedSizeArray {};
- Historical note: before C++17, template template parameter inner type parameters required
class, nottypename.
FAQ
Is template<class T> faster than template<typename T>?
No. They are equivalent for normal type template parameters and produce no performance difference.
Should I use class or typename in modern C++?
Either is correct. Follow the existing style of the project or team. Many developers prefer typename because it reads clearly as “this is a type.”
Does class T mean that T must be an object-oriented class?
No. T can be int, double, an enum, a pointer, a class, or another valid type.
Why does C++ sometimes require typename?
Inside a template, a name such as T::value_type depends on T. typename tells the compiler that this dependent name is a type.
Can I replace every typename with class?
No. They are interchangeable only when declaring a type template parameter. In typename T::value_type, only typename performs the required disambiguation.
Is typename allowed for template template parameters?
In C++17 and later, it can be used in the inner type parameter list. Before C++17, class was required in that position.
Does the choice affect template specialization or overload resolution?
No. For ordinary type parameters, the compiler treats the declarations identically.
Mini Project
Description
Build a small generic utility that returns the first value from a container and demonstrates both meanings of typename: declaring a type parameter and identifying a dependent nested type. This pattern resembles helper functions used in data-processing and library code.
Goal
Create a reusable firstOrDefault function that works with standard containers and returns their element type.
Requirements
- Declare the container parameter as a template type parameter.
- Return the container's
value_type. - Return a caller-provided default value when the container is empty.
- Demonstrate the function with
std::vector<int>andstd::list<std::string>. - Use
typenamecorrectly for the dependentvalue_type.
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.