What is the difference between "typename" and "class" template parameters?
Categories:
Typename vs. Class in C++ Templates: Understanding the Distinction
Explore the subtle yet crucial differences between typename
and class
when declaring template type parameters in C++, and learn when to use each keyword for robust and correct template code.
When working with C++ templates, you often encounter two keywords for declaring type parameters: class
and typename
. While they frequently appear interchangeable, especially in simple cases, there's a fundamental distinction that becomes critical in more complex template scenarios, particularly when dealing with dependent types. Understanding this difference is key to writing correct, robust, and portable template code.
The Basic Equivalence: Template Type Parameters
In the most common use case, when declaring a template type parameter, class
and typename
are indeed equivalent. Both indicate that the parameter T
(or whatever you name it) is a type. This applies to both function templates and class templates.
template <class T>
class MyClass1 {};
template <typename T>
class MyClass2 {};
template <class T>
T max(T a, T b) {
return (a > b) ? a : b;
}
template <typename T>
T min(T a, T b) {
return (a < b) ? a : b;
}
int main() {
MyClass1<int> obj1;
MyClass2<double> obj2;
int m1 = max(5, 10);
double m2 = min(3.14, 2.71);
return 0;
}
Basic usage where class
and typename
are interchangeable for template type parameters.
The Crucial Distinction: Dependent Types
The real difference emerges when you need to refer to a dependent type name inside a template. A dependent type name is a type that depends on a template parameter. When you access a nested type within a template parameter, the compiler needs to know if that name refers to a type or a non-type member (like a static member variable or an enum value). By default, the compiler assumes it's a non-type. To explicitly tell the compiler that a dependent name is indeed a type, you must use typename
.
flowchart TD A[Template Parameter Declaration] --> B{Is it a simple type parameter?} B -->|Yes| C["class T" or "typename T" (Equivalent)] B -->|No| D{Is it a dependent type name?} D -->|Yes| E["typename T::NestedType" (Required)] D -->|No| F["T::NestedValue" (No typename needed)]
Decision flow for choosing between class
and typename
in C++ templates.
template <typename T>
class Container {
public:
// T::iterator is a dependent type name
// Without 'typename', this would be a compilation error
typename T::iterator begin() { /* ... */ }
typename T::iterator end() { /* ... */ }
// Example of a non-type dependent member (no typename needed)
static const int value = T::static_member_value;
};
// Example class to use with Container
#include <vector>
int main() {
std::vector<int> myVec = {1, 2, 3};
Container<std::vector<int>> c;
// This would compile if Container had begin/end implementations
// auto it = c.begin();
return 0;
}
Illustrating the necessity of typename
for dependent type names.
typename
for all template type parameters unless you have a specific reason not to. This makes your code more consistent and helps avoid potential issues with dependent types, even if they don't immediately appear in your current code.Why the Ambiguity? The 'Two-Phase Lookup'
The need for typename
stems from C++'s 'two-phase lookup' mechanism for templates. During the first phase, the compiler parses the template definition without knowing the concrete types for the template parameters. It identifies non-dependent names. During the second phase, after instantiation with actual types, it resolves dependent names.
When the compiler encounters T::NestedName
in the first phase, it doesn't know what T
will be. NestedName
could be a type (like std::vector<int>::iterator
) or a static data member (like std::vector<int>::max_size
). To resolve this ambiguity, C++ requires you to explicitly state typename
if NestedName
is intended to be a type. This helps the compiler correctly parse the template definition before it knows the actual types.
typename
for a dependent type name is a common compilation error, often resulting in messages like 'expected a type name' or 'missing 'typename' before dependent type name'.Summary of Usage
To summarize the usage of class
and typename
in C++ templates:
class
: Can be used to declare a template type parameter (e.g.,template <class T>
). It's a historical artifact from C++'s early days when templates were often associated with classes. It cannot be used for dependent type names.typename
: Can also be used to declare a template type parameter (e.g.,template <typename T>
). Additionally, and crucially, it must be used to disambiguate dependent names that refer to types within a template (e.g.,typename T::NestedType
).
Given its broader applicability and ability to handle dependent types, typename
is generally the preferred keyword for declaring template type parameters in modern C++.