Why is there a "class" in "template <class x>"?
Keyword class
means the same as keyword typename
. They both indicate that T is a type.
The only difference between the keyword class
and typename
is what class
can be used to provide a template pattern template argument pattern, whereas typename
can not. Consider:
template<template <class T> class U> // must be "class" std::string to_string(const U<char>& u) { return std::string(u.begin(),u.end()); }
The only thing you can add instead of keywords class
or typename
is an integral type. For example:
template<std::size_t max> class Foo{...}; ... Foo<10> f;
For a specific example, see the std::bitset<N>
standard library.
source share
To define a template parameter, you either need to tell the compiler that the parameter is a type or a value.
At the beginning...
If I recall correctly, the C ++ committee was reluctant to add a new keyword to the C ++ language, and so they decided to allow the following notation:
template<int I> int getTwice() { return I * 2 ; } template<class T> std::string getType(const T & t) { return typeid(t).name() ; } void doSomething() { std::cout << "25 : " << getTwice<25>() << std::endl ; std::cout << "5 : " << getTwice<5>() << std::endl ; std::cout << "type(25) : " << getType(25) << std::endl ; std::cout << "type(25.5) : " << getType(25.5) << std::endl ; std::cout << "type(abc) : " << getType("abc") << std::endl ; }
What are the outputs, in g ++:
25 : 50 5 : 10 type(25) : i type(25.5) : d type(abc) : A4_c
The first designation was a pattern over a value. So we have a value type in our template declaration:
// "I" is the value, and "int" is the type of the value template <int I>
The second notation was pattern over an unknown type, and the fact that the type was not "known" was flagged with the "class" keyword. So in this context "class" means "type".
// "T" is a type... And "class" is the "this-is-a-type" keyword template <class T>
You will notice that with the second note, despite the class keyword, T could be ... int or some other built-in type. But then it's better to have that curiosity than to add a new keyword, don't you agree? ...
Oops ...
Everything was fine and good until someone wrote the following code:
template<class T> // T could be any STL container, for example a vector<char> void printContainerData(const T & t) { std::cout << "aVector:" ; for(T::const_iterator it = t.begin(), itEnd = t.end(); it != itEnd; ++it) { std::cout << " " << (*it) ; } std::cout << std::endl ; }
Where T :: const_iterator is a type, of course ... But then it can be a static member of the class of type T and therefore a value. The compiler can be quite confusing.
At the end...
The solution was to tell the compiler that T :: const_iterator is indeed of type ... Which would result in a notation like this:
for(class T::const_iterator it = t.begin(), // etc.
But that was considered impossible / correct (class refers to class declarations, no?). So by dragging their feet, they decided that the keyword was really needed to tell the compiler that this symbol was a type, not a value.
"type" was left out, I think, because creating a keyword would break a lot of code. So typename was used instead. With typename we can write:
for(typename T::const_iterator it = t.begin(), // etc.
And for consistency, we have to use:
template <typename T>
When T is assumed to be a type and not a value. But for compatibility reasons the old notation is:
template <class T>
still resolved.
What about??
eben suggested the answer above, the answer I wanted to comment on because it's quite interesting:
template<template <class T> class U> // must be "class" std::string to_string(const U<char>& u) { return std::string(u.begin(),u.end()); }
I will only comment on its "meaning" (this code cannot be used with STL containers in my g ++ compiler, but I donβt think it was not): One point, it puts a constraint on U saying: U is a templated class type T. This is part:
template <class T> class U
What can also be written:
template <typename T> class U
Because U is really and only a class (not a built-in type) and T is a type, any type.
The next line says that U is specialized over char:
std::string to_string(const U<char>& u)
So this "generic code" will only work for U if U is declared as:
template<typename T> class U { // Etc. } ;
And U is induced via char:
U<char> u ; // etc. to_string(u)
But one thing has been forgotten: Eben's designation can be written in two ways:
template<template <class T> class U> std::string to_string(const U<char>& u) template<template <typename T> class U> std::string to_string(const U<char>& u)
The second "class" keyword is not the "type" keyword. It is a type that is a class template over T. So the notation is confusing.
Another way to write Eben code that removes the restrictions above would be something like:
template<typename U> std::string to_string(const U & u) { return std::string(u.begin(),u.end()); }
And let the compiler do its magic:
std::list<char> myList ; // etc. std::cout << to_string(myList) << std:endl ;
(The Eben code didn't work with STL containers that have a "char" pattern on my g ++ compiler for example ...)
source share
You can also use the following template arguments:
- any integral type
short, int, long, bool
, etc. - pointers to objects or functions
- references to objects or functions
- pointers to member objects or member functions
A few examples:
template<typename T, int N=4, bool Flag> class myClass { /*...*/ }; template<typename T, const int& Reference, myClass * Pointer> class someClass { /*...*/ }; template<typename T, int (T::*MemberFunctionPtr)(int, int)> class anotherClass { /*...*/ };
source share