Question

C++ Template Parameters: class vs typename

cpptemplates

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

Take Quiz

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:

  1. template <typename T> declares T as a placeholder for a type.

  2. The call add(3, 4) supplies two int arguments.

  3. The compiler deduces that T is int.

  4. It produces an int version of the function conceptually equivalent to:

    int add(int left, int right) {
        return left + right;
    }
    
  5. The function returns 7, which is stored in result.

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>, and std::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> or Cache<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-name typename may 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"};
Take Quiz

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 typename for all type parameters because it makes the intent explicit: the parameter represents a type.
  • Use class for type parameters because it is shorter and traditional in generic C++ code.
  • Use class in template declarations but reserve typename for 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

SituationclasstypenameNotes
Ordinary type template parameterYesYesEquivalent.
Function template type parameterYesYesEquivalent.
Class template type parameterYesYesEquivalent.
Dependent qualified type, such as T::value_typeNoYestypename may be required to identify it as a type.
Non-type template parameter, such as int NNoNoUse 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.

Take Quiz

Cheat Sheet

// Equivalent type parameter declarations
template <class T>
struct A {};

template <typename T>
struct B {};
  • In a type template parameter, class and typename have the same meaning.
  • class T does not restrict T to class types.
  • Choose one based on your codebase's style guide.
  • typename also 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, not typename.

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.

Take Quiz

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> and std::list<std::string>.
  • Use typename correctly for the dependent value_type.
Take Quiz

Keep learning

Related questions

View more