Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ template class = typename

Tags:

c++

templates

what template <class = typename T::type> means? Can you refer me to some blog, specification describing this?

The question originaly came from explanation of sfinae on cpp reference

template <typename A>
struct B { typedef typename A::type type; };

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);
like image 441
cavetrolldigger Avatar asked Mar 21 '18 13:03

cavetrolldigger


1 Answers

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.

like image 108
Guillaume Racicot Avatar answered Oct 01 '22 06:10

Guillaume Racicot