对 C++ 嵌套依赖类型名称感到困惑

Confused about C++ nested dependent type name

Effective C++告诉我遇到嵌套的依赖类型名时最好使用typename

下面的示例代码很容易理解:

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;
  ...
}

然而,在模板 class BST 中添加使用别名后,关键字 typename 似乎不再是必需的了。

在这里你可以看到:

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;
  ...
}

有谁能弄明白吗?

该效果不直接通过 using 与类型别名相关联,它是当前实例化成员的名称查找的结果。

BST 中,BSTBST<ElementType> 表达式都引用当前实例化,并且可以在不需要前缀 typename 的情况下找到它的成员,你可以做:

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

结果是一样的。但现在假设 some_func 也是一个模板成员函数,定义为:

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)
}

现在 (1) 和 (2) 都不会编译,因为 B<T> 不再是当前实例,因此在这些情况下你需要 typename.

标准的相关部分[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. [...]