c + + -- typename keyword

Before introducing typename, you should understand several concepts:

1. Qualified and unqualified names

A qualified name, as the name suggests, is a name that defines a namespace.

#include <iostream>

int main()  
{
    std::cout << "Hello world!" << std::endl;
}

std:: defines the namespace of std, so it is called qualified name.

#include <iostream>
using namespace std;

int main()  
{
    cout << "Hello world!" << endl;
}

When using namespace std is used, std:: qualification is no longer required. cout and endl are called unrestricted names at this time.

2. Dependent name and independent name

Dependent names refer to names that depend on template parameters. On the contrary, independent names refer to names that do not depend on template parameters.

template <class T>
class MyClass {
    int i;
    vector<int> vi;
    vector<int>::iterator vitr;

    T t;        //Because it depends on the template parameter T, its type can only be known when the template is instantiated
    vector<T> vt;
    vector<T>::iterator viter;
};

T. Vector < T > and vector < T >:: iterator is called dependent name, int, vector < int > and vector < int >:: iterator is called independent name.

3. Class scope

There are three ways to access the name in a class outside of the class:

1) Static data member

2) Static member function

3) Nested type

struct MyClass {
    static int A;
    static int B();
    typedef int C;
}

It can be represented by MyClass::A, MyClass::B and MyClass::C respectively

4. Reasons for introducing typename

Consider the following example:

template<typename T>
 
void print2nd(const T& container)
 
{
 
    //...
 
    //iterator may be understood by the compiler as the static member variable of C, x is a variable, and the following is the multiplication of the two variables
 
    T::iterator* x;
    
    //...

}
 

The meaning of this Code: define a pointer x, the type it points to is the iterator in the scope T of the containing class.

As mentioned earlier, T::iterator may be a static data member, as shown below:

struct ContainsAnotherType {
    static int iterator;
    // ...

};

At this time, T::iterator* x is instantiated by the compiler as ContainsAnotherType::iterator * iter. At this time, it becomes a multiplication expression, and the compiler will report an error.

4. typename

4.1 basic use

There is a sentence in the c + + standard:

A name used in a template declaration or definition and that is dependent on a template-parameter is assumed not to name a type unless the applicable name lookup finds a type name or the name is qualified by the keyword typename.

For the name that depends on the template parameter used for template definition, the compiler will treat the name as a type only if the type name exists in the instantiated parameter or is decorated with the typename keyword before the name. In addition to the above two cases, it will never be regarded as a type.

The above example can be modified as:

template<typename T>
 
void print2nd(const T& container)
 
{
 
    typename T::iterator* x;  //Let the compiler determine that T::iterator is a type, not a variable, without waiting for instantiation
    
    //...

}
 

4.2 usage rules

typename is prohibited in the following cases:

1) In addition to the template definition, that is, typename can only be used in the template definition

2) Unqualified types, such as int, vector < int > and so on

3) In the base class list, such as class derived: public base < T >:: nested, typename cannot be added before public base < T >:: nested

4) Constructor's initialization list

template<typename T>
 
class Derived :public Base<T>::Nested //typename cannot be used here
 
{
 
public:
 
    explicit Derived(int)
 
    :Base<T>::Nested(x)//typename cannot be used here
 
    {
 
        typename Base<T>::Nested temp; //typename can be used here
 
    }
 

4.3 use in traits

Code example:

template<typename IterT>
 
void workWithIterator(IterT iter)
 
{
 
    typename std::iterator_traits<IterT>::value_type temp(*iter);
 
    //...
 
}

Here we use the iterator_ Traits < > template class is actually a kind of traits class. We pass an iterator type to instantiate it, so we can use its value_type extracts the type of container that the iterator refers to. For example:

1) if IterT is list < string >:: iterator, then value_type stands for string, and the type of temp is string

2) if IterT is vector < int >:: iterator, then value_type stands for int, and the type of temp is int

Because value_type is also an embedded type, so we need to use typename to declare it as a type

If the above code is complex, we can also use it with typedef, which is an alias for declaring a type.

template<typename IterT>
 
void workWithIterator(IterT iter)
 
{
 
    typedef typename std::iterator_traits<IterT>::value_type value_type; //Declare an alias for a type
 
    value_type temp(*iter); //Defining variables using types
 
    //...
 
}

There are many similar examples in stl source code, such as:

typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;

It means: will__ type_ Traits < T > has in this template class_ trivial_destructor defines a nested type called trivial_ Alias of destructor

5 Summary

Effective C + + clause 42: template and generic programming (understand the dual meaning of typename) is summarized as follows:

1) When declaring the template parameter, the prefix keywords class and typename are interchangeable

2) Please use the keyword typename to identify the nested dependent type name; However, it cannot be used as a base class modifier in base class lists or member initialization list

 

Tags: C++

Posted by aouriques on Tue, 19 Apr 2022 08:12:07 +0930