Use 'class' or 'typename' for template parameters?
Stan Lippman talked about this here. I thought it was interesting.
Summary: Stroustrup originally used class
to specify types in templates to avoid introducing a new keyword. Some in the committee worried that this overloading of the keyword led to confusion. Later, the committee introduced a new keyword typename
to resolve syntactic ambiguity, and decided to let it also be used to specify template types to reduce confusion, but for backward compatibility, class
kept its overloaded meaning.
Difference of keywords 'typename' and 'class' in templates?
typename
and class
are interchangeable in the basic case of specifying a template:
template<class T>
class Foo
{
};
and
template<typename T>
class Foo
{
};
are equivalent.
Having said that, there are specific cases where there is a difference between typename
and class
.
The first one is in the case of dependent types. typename
is used to declare when you are referencing a nested type that depends on another template parameter, such as the typedef
in this example:
template<typename param_t>
class Foo
{
typedef typename param_t::baz sub_t;
};
The second one you actually show in your question, though you might not realize it:
template < template < typename, typename > class Container, typename Type >
When specifying a template template, the class
keyword MUST be used as above -- it is not interchangeable with typename
in this case (note: since C++17 both keywords are allowed in this case).
You also must use class
when explicitly instantiating a template:
template class Foo<int>;
I'm sure that there are other cases that I've missed, but the bottom line is: these two keywords are not equivalent, and these are some common cases where you need to use one or the other.
C++ template class = typename
First, I'll explain typename T::type
. This is simply the access of a member type. Here's an example of accessing a member type:
struct foo {
using bar = int;
};
int main() {
foo::bar var{};
// var has type int
}
So why the typename
? It simply means we want to access a type. Since we are in a template and T
is an unknown type, and foo::bar
could also mean accessing a static variable. To disambiguate, we denote that we effectively want to access a type by explicitly typing typename
.
Okay, now what does the class =
means?
The class =
means the same thing as typename =
. When declaring template type parameters, we introduce then using class
or typename
:
template<typename A, typename B>
struct baz {};
But as any parameters in C++, the name is optional. I could have wrote this and the following is completely equivalent:
template<typename, typename>
struct baz {};
Also, you know in function parameters, we can specify default values? Like that:
void func(int a, int b = 42);
int main () {
func(10); // parameter b value is 42
// We are using default value
}
We can also omit parameter names:
void func(int, int = 42);
Just like function parameters, template parameters can have it's name omitted, and can have a default value.
template<typename, typename = float>
struct baz {};
baz<int> b; // second parameter is float
Putting it all together
Now we have this declaration:
template <
class T,
class = typename T::type, // SFINAE failure if T has no member type
class U = typename B<T>::type // hard error if T has no member type
// (guaranteed to not occur as of C++14)
> void foo (int);
Here we declare a function that takes an int as parameter, and has three template parameter.
The fist parameter is a simple named parameter. The name is T
and it's a type template parameter. The second is also a type parameters, but it has no name. However, it has a default value of T::type
, which is a member type of T
. We are explicitly telling the compiler that T::type
must be a member type of T
by specifying typename
. The third parameter is similar to the second.
This is where SFINAE kicks in: when a default parameter is used, but T::type
as as a member type don't exist, how can you assign the second template parameter to that? We can't. If T::type
don't exists, we cannot assign the second template parameter. But instead of making it an error, the compiler will simply try another function, because there is a chance another function would be callable.
This is quite similar to simple overloading. You have the f
function. It takes a float
parameter, an another overload that takes a std::string
. Imagine you call f(9.4f)
. Does the compiler choke because a std::string
is not constructible from a float
? No! The compiler ain't stupid. It will try another overload, and will find the float
version and call it. In SFINAE a similar analogy can be made. The compiler won't stop because there's some overload somewhere that needs an undefined type in a template parameter. It will try another overload.
Difference between Class and Typename for C++ template
For designating (type) template parameters, the two are exactly identical, just like int
/signed int
, or &&
/and
: template <typename>
/template <class>
.
A curious restriction applies to template template parameters up to C++14:
template <template <typename> class Tmpl> struct Foo;
// ^^^^^
Here only the keyword class
is allowed to designate the template template parameter.
After C++14, you will be able to consistently use either class
or typename
everywhere:
template <template <typename> typename Tmpl> struct Foo;
how can i use a template class as another class's typename?
You should define T
as a template template parameter, e.g.
template<template<typename> class T>
class B {
B() : a(new T<int>), b(new T<char>) {}
T<int>* a;
T<char>* b;
};
Then you can specify other class template like A
as the template argument such as B<A> b;
.
BTW: From C++17 you can use keyword typename
for template template parameter declaration too. i.e.
template<template<typename> typename T>
class B {
B() : a(new T<int>), b(new T<char>) {}
T<int>* a;
T<char>* b;
};
C++ Passing a tempate typename class as function parameter
You're not declaring list
as a template template parameter
correctly.
template <template <typename> class list, typename Type>
void insertion( list<Type>& L )
{
...
}
Reference: http://en.cppreference.com/w/cpp/language/template_parameters
Why C++17 have two keywords(class and typename) for the same purpose in templates
This is a subjective question. Personally I would tend to agree that having class
here at all is poor form, and typename
should have been the only keyword allowed.
But that's just my opinion. Perhaps this usage of class
will be deprecated in the future; perhaps there are sufficient people who like to write class
for whatever reason, that it won't be.
At the end of the day, it's hardly the most pressing issue to affect C++, which is probably the real reason that the committee ended up relaxing restrictions rather than adding new ones.
If you feel strongly that this usage of class
should be deprecated, feel free to propose this to the working group!
By the way, the usage of static
you refer to was actually undeprecated in C++11.
How to get template class template parameter?
You can create traits to extract that information without changing originel types.
template <typename T>
struct template_parameter;
template <template <typename ...> class C, typename T>
struct template_parameter<C<T>>
{
using type = T;
};
template <typename T>
using template_parameter_t = typename template_parameter<T>::type;
and then
template <typename templateClass>
void f()
{
if constexpr (std::is_same_v<template_parameter_t<templateClass>, int>)
std::cout << "int";
else if constexpr (std::is_same_v<template_parameter_t<templateClass>, double>)
std::cout << "double";
else
std::cout << "Unknown type";
}
Demo
Related Topics
How to Output Coloured Text to a Linux Terminal
What's the Difference Between _Pretty_Function_, _Function_, _Func_
Proper Stack and Heap Usage in C++
How to Make Cmake Output into a 'Bin' Dir
Does It Make Any Sense to Use Inline Keyword With Templates
Why Can't I Use Float Value as a Template Parameter
How Can a Windows Service Execute a Gui Application
Variable Initialization in C++
Std::Endl Is of Unknown Type When Overloading Operator≪≪
Getting Size of Array from Pointer C++
How to Build Qt For Visual Studio 2010
How to Check If a Cpu Supports the Sse3 Instruction Set
Why Does Pointer Decay Take Priority Over a Deduced Template