use of typename keyword

I was going through tutorials and read that typename keyword is required whenever we need to define a object as follows:

typename A::C d;
where C is a nested class of A and d is the object.

However, all examples I have come through making use of iterator have used the following syntax:

list<Product>::iterator a;
where Product is some class and a the object created.

Why is the keyword typename not used in this case and only applies for the member access * case.
The typename keyword is used in templates, where without it the compiler doesn't know that it's a type name instead of member data.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Example
{
public:

    class iterator { /* ... */ }; // Example::iterator is type name

    int age; // Example::age member data
};

template <class E>
void func(E e)
{
    E::iterator i; // compiler confused, what is E::iterator?

    typename E::iterator i; // E::iterator is a type name so we can define i
}


"Real" example: a function template which can print the contents of some STL containers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <iomanip>

template <class STLContainer>
void very_dumb_print(STLContainer c)
{
    typename STLContainer::const_iterator ci;

    for (ci = c.begin(); ci != c.end(); ++ci)
        std::cout << std::setw(5) << *ci;

    std::cout << std::endl;
}

#include <vector>
#include <list>
#include <set>

int main()
{
    std::vector<int> vi;
    std::list<float> lf;
    std::set<char> sc;

    vi.push_back(1);
    vi.push_back(20);
    vi.push_back(3);
    vi.push_back(40);
    vi.push_back(5);

    lf.push_back(0.0f);
    lf.push_front(1.5f);
    lf.push_back(2.0f);
    lf.push_front(3.5f);

    sc.insert('b');
    sc.insert('a');
    sc.insert('d');
    sc.insert('c');

    very_dumb_print(vi);
    very_dumb_print(lf);
    very_dumb_print(sc);
}
    1   20    3   40    5
  3.5  1.5    0    2
    a    b    c    d

Last edited on
In a template that we write, there are two kinds of names that could be used - dependant names and non- dependant names. A dependant name is a name that depends on a template parameter; a non-dependant name has the same meaning irrespective of what the template parameters are.

For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template< typename T > void foo( T& x, std::string str, int count )
{
      // these names are looked up during the second phase
      // when foo is instantiated and the type T is known
      x.size(); // dependant name (non-type)
      T::instance_count ; // dependant name (non-type)
      typename T::iterator i ; // dependant name (type)
      
      // during the first phase, 
      // T::instance_count is treated as a non-type (this is the default)
      // the typename keyword specifies that T::iterator is to be treated as a type.

      // these names are looked up during the first phase
      std::string::size_type s ; // non-dependant name (type)
      std::string::npos ; // non-dependant name (non-type)
      str.empty() ; // non-dependant name (non-type)
      count ; // non-dependant name (non-type)
}


What a dependant name refers to could be something different for each different instantiation of the template. As a consequence, C++ templates are subject to "two-phase name lookup". When a template is initially parsed (before any instantiation takes place) the compiler looks up the non-dependent names. When a particular instantiation of the template takes place, the template parameters are known by then, and the compiler looks up dependent names.

During the first phase, the parser needs to know if a dependant name is the name of a type or the name of a non-type. By default, a dependant name is assumed to be the name of a non-type. The typename keyword before a dependant name specifies that it is the name of a type.
Topic archived. No new replies allowed.