Use 'Class' or 'Typename' For Template Parameters

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



Leave a reply



Submit