Search code examples
c++templatesusingtypename

Confused about C++ nested dependent type name


Effective C++ told me that I'd better use typename when encountered nested dependent type name.

The following example code is easy to understand:

template <typename ElementType>
class BST {
private:
  class LinkNode {
  public:
    ElementType data;
    LinkNode *left, *right;
    explicit LinkNode() {}
  };

public:
  void some_func();
}

template <typename ElementType>
void BST<ElementType>::some_func() {
  // or `using NodePtr = typename BST<ElementType>::LinkNode *;`
  typedef typename BST<ElementType>::LinkNode * NodePtr;
  ...
}

However, after I added using aliases in the template class BST, it seemed that keyword typename is not neccessary anymore.

Here you can see:

template <typename ElementType>
class BST {
private:
  class LinkNode {
  public:
    ElementType data;
    LinkNode *left, *right;
    explicit LinkNode() {}
  };

  using NodePtr = LinkNode *; // the only difference between these two code blocks

public:
  void some_func();
}

template <typename ElementType>
void BST<ElementType>::some_func() {
  // typename is not neccessary here!
  BST<ElementType>::NodePtr ptr;
  ...
}

Does anyone could figure out that?


Solution

  • That effect is not directly tied to type alias through using, it's a result of name lookup for member of the current instantiation.

    Inside BST both BST and BST<ElementType> expression refer to the current instantiation and the members of it can be found without the need of the prefix typename you could do:

    template <typename ElementType>
    void BST<ElementType>::some_func() {
        BST::NodePtr ptr; // or 
        BST<ElementType>::LinkNode * ptr2;
    }
    

    resulting in the same thing. But now let assume that some_func is also a template member function defined as:

    template <typename ElementType>
    struct BST {
        class LinkNode { /*...*/ };
    
        using NodePtr = LinkNode *;
    
        template <typename T>
        void some_func();
    };
    
    
    template <typename ElementType>
    template <typename T>
    void BST<ElementType>::some_func() {
        BST<T>::NodePtr ptr;     // (1)
        BST<T>::LinkNode * ptr2  // (2)
    }
    

    Now neither (1) nor (2) will compile because B<T> is no longer the current instantiation, hence in these cases you need typename.

    The relevant part of the standard [temp.res]/7:

    Within the definition of a class template or within the definition of a member of a class template following the declarator-id, the keyword typename is not required when referring to the name of a previously declared member of the class template that declares a type or a class template. [...]