0

When I was reading stdc++ code, I felt confused about the below lines. Here is a piece of code from stl_list.h.

template <class _Tp, class _Alloc>                                      
class _List_base                                                        
  : public _List_alloc_base<_Tp, _Alloc,                                
                            _Alloc_traits<_Tp, _Alloc>::_S_instanceless>
{                                                                       
public:                                                                 
  typedef _List_alloc_base<_Tp, _Alloc,                                 
                           _Alloc_traits<_Tp, _Alloc>::_S_instanceless> 
          _Base;  /* Is _Base a kind of type?*/                              
  typedef typename _Base::allocator_type allocator_type;                

  _List_base(const allocator_type& __a) : _Base(__a) {/* How can a type be initiated like this?*/                  
    _M_node = _M_get_node();                                            
    _M_node->_M_next = _M_node;                                         
    _M_node->_M_prev = _M_node;                                         
  }
}                                                              

What confused me is _Base. This name was defined by typedef. I think it is as kind of type. But _Base appears in initialization list. How can I understand this usage? Or you please paste some useful links for me.

Nacho
  • 1,104
  • 1
  • 13
  • 30
Hector.Z
  • 31
  • 3

2 Answers2

1

It is used to define a custom type, based on a previously existing type. To provide an alias for a type you may say. typedef could be read as "define type" or "type-definition".

You can have "chained" typdefs, i.e.:

typedef int Id;     // Define new type "Id"
typedef Id UserId;  // Define new type "UserId" based on a previously existing type
// This is possible since "Id" was already defined

And then just use it:

UserId uid = 0; //uid is a variable of type UserId, which is an alias for int

In the example you provided every time you use _Base you are using _List_alloc_base<_Tp, _Alloc, _Alloc_traits<_Tp, _Alloc>::_S_instanceless> under the hood, but in a shorter manner. Using that alias results in a cleaner and more compact block of code.

You may notice that the example also uses typedef typename in the following line:

typedef typename _Base::allocator_type allocator_type;

Note that _Base can be used in that line because it was already defined in the previous line. Refer to this question for clarification on the combined use of typedef and typename, read this answer too.


From this link:

The formula to follow is:

typedef [attributes] DataType AliasName;

The typedef keyword is required. The attribute is not. The typedef keyword can be followed by any C++ built-in data type, including int, short, signed, unsigned, char, signed char, unsigned char, double, long, or long double. The data type can also be an existing class that either is provided by one of the libraries that ship with the C++ compiler. For example, it can be the string class. The data type can also be a pointer to a known type.

Even though this is a definition you will encounter many times for this keyword, it might be easier to think of it as typedef declaration; as pointed out by Matteo Italia in the comments below.

Community
  • 1
  • 1
Nacho
  • 1,104
  • 1
  • 13
  • 30
  • The `typedef [attributes] DataType AliasName;` is moot, and confused me to no end when I initially learned C and C++, because (for example) `typedef`s that involved function pointers made no sense at all under those fake premises. The right way to look at a `typedef` is: `typedef declaration;`, where declaration is exactly as you would declare a variable, only instead of a *variable* it declares an *alias* for its type. – Matteo Italia Apr 27 '16 at 14:38
1

A typedef declares an alias for a type - a shorthand, if you prefer.

Its syntax is exactly as if you were declaring a variable, but instead of a variable it creates an alias for the specified type:

int a;         // a is a variable of type int
typedef int B; // B is an alias for type int

(notice that often they say typedef original-type alias, but it's an incorrect rule - if fails for function pointers, for example - and it's one more rule to remember for no reason)

As for static members, the type alias is scoped - inside the class it's accessible without other qualifiers, from the outside it requires qualification (_List_base::_Base).


Coming to your specific case, it's just a shorthand to refer to the base class, which is _List_alloc_base<_Tp, _Alloc, _Alloc_traits<_Tp, _Alloc>::_S_instanceless> and would be definitely cumbersome to write fully every time.

It appears in the initialization list because the constructor is invoking the constructor of the base class - i.e. _Base - passing the parameter __a.

This could be easier to see if you could somehow "enlarge" the scope of the typedef and write:

template <class _Tp, class _Alloc>                                      
class _List_base                                                        
  : public _Base
{                                                                       
public:                                                                 
  typedef _List_alloc_base<_Tp, _Alloc,                                 
                           _Alloc_traits<_Tp, _Alloc>::_S_instanceless> 
          _Base;
  typedef typename _Base::allocator_type allocator_type;                

  _List_base(const allocator_type& __a) : _Base(__a) {
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299