拆分模板 class 嵌套模板的定义和声明失败

Splitting template class definitions and declarations fails for nested templates

我知道我想要什么,但我不知道如何告诉编译器我想要什么。我在 .h 和 .cpp 文件中拆分了声明和方法定义,但是 .cpp 文件包含在 header 中,所以我所做的只是单独的声明和定义。

header 文件 (avltree.h) - 它包含源文件!:

template < class KEY_T, class DATA_T >
class CAvlTree {
    public:

        //----------------------------------------

        template < class KEY_T, class DATA_T >
        class CNode;

        typedef CNode< KEY_T, DATA_T> * tNodePtr;

        template < class KEY_T, class DATA_T >
        class CNode {
            public:
                KEY_T       key;
                DATA_T      data;
                CNode< KEY_T, DATA_T > *    left;
                CNode< KEY_T, DATA_T > *    right;
                char        balance;

                CNode() : left(nullptr), right(nullptr), balance(0) {}

                CNode(KEY_T key, DATA_T data) : 
                    key (key), data (data), left(nullptr), right(nullptr), balance(0) {}
            };

        //----------------------------------------

        template < class KEY_T, class DATA_T >
        struct tAvlInfo {
            CNode< KEY_T, DATA_T> * root;
            CNode< KEY_T, DATA_T> * current;
            KEY_T       key;
            bool        isDuplicate;
            bool        branchChanged;
        };

        typedef bool (* tNodeProcessor) (CNode< KEY_T, DATA_T> * nodePtr);

    private:

        tAvlInfo< KEY_T, DATA_T >   m_info;

        //----------------------------------------

    public:

        DATA_T* Find(KEY_T& key);

    private:

        CNode< KEY_T, DATA_T> * AllocNode(void);
};

#include "avltree.cpp"

源文件(avltree.cpp):

template < typename KEY_T, typename DATA_T >
DATA_T* CAvlTree< KEY_T, DATA_T >::Find (KEY_T& key)
E0276   name followed by '::' must be a class or namespace name
{
   CNode* root;

   for (CNode* node = m_info.root; node; ) {
       if (key < node->key)
           node = node->left;
       else if (key > root->key)
           node = node->right;
       else {
           m_info.current = node;
           return &node->data;
       }
   }
return nullptr;
}


template < typename KEY_T, typename DATA_T >
CNode* CAvlTree< KEY_T, DATA_T >::AllocNode (void)
E0020   identifier "CNode" is undefined
E0864   CAvlTree is not a template
{
    if (m_info.current = new CNode < KEY_T, DATA_T >) {
        m_info.branchChanged = true;
        return m_info.current;
    }
    return nullptr;
}

编译器所说的(VS 2019 社区,启用 c++2020):

E0276   name followed by '::' must be a class or namespace name
E0020   identifier "CNode" is undefined
E0864   CAvlTree is not a template

我不知道如何以正确的方式写下来。请赐教。

我有一个模板的类似代码 class,它具有一个有效的类型名称,但无法从中得出如何对两个类型名称执行此操作的结论。

在我的源代码中使用 tNodePtr 类型而不是 CNode< KEY_T, DATA_T > * 也不起作用。

顺便说一句,我知道编译器不会仅仅因为我使用相同的名称就从 CAvlTree 声明中“转移”KEY_T 和 DATA_T。

你的问题是:

template < class KEY_T, class DATA_T >
class CNode;

那些KEY_TDATA_TCAvlTree中的一个无关。

clang 和 gcc 甚至会因为名称阴影而为那些发出错误(我不知道 VS 是否也这样做)。

由于 CNode 是一个 class 模板,编译器希望您在所有使用 [= 的地方为其提供模板参数(就像您为 CNode< KEY_T, DATA_T> * AllocNode(void); 所做的那样) 16=].

但是如果你在代码中对每个 CNode 使用 KEY_TCAvlTreeDATA_T,那么问题是你为什么要定义 CNode首先作为 class 模板。

这部分代码表示您不希望 CNode 成为 class 模板:

template < class KEY_T, class DATA_T >
class CNode;

typedef CNode< KEY_T, DATA_T> * tNodePtr;

// …
CNode< KEY_T, DATA_T> * AllocNode(void);

template < class KEY_T, class DATA_T > struct tAvlInfo似乎也是如此。

因此,我认为您想要的代码应该如下所示:

template <class KEY_T, class DATA_T>
class CAvlTree {
public:
  //----------------------------------------
  class CNode {
  public:
    KEY_T key;
    DATA_T data;
    CNode *left;
    CNode *right;
    char balance;

    CNode() : left(nullptr), right(nullptr), balance(0) {}

    CNode(KEY_T key, DATA_T data)
        : key(key), data(data), left(nullptr), right(nullptr), balance(0) {}
  };

  //----------------------------------------

  struct tAvlInfo {
    CNode *root;
    CNode *current;
    KEY_T key;
    bool isDuplicate;
    bool branchChanged;
  };

  typedef bool (*tNodeProcessor)(CNode *nodePtr);

private:
  tAvlInfo m_info;

  //----------------------------------------

public:
  DATA_T *Find(KEY_T &key);

private:
  CNode *AllocNode(void);
};

template <typename KEY_T, typename DATA_T>
DATA_T *CAvlTree<KEY_T, DATA_T>::Find(KEY_T &key) {
  CNode *root;

  for (CNode *node = m_info.root; node;) {
    if (key < node->key)
      node = node->left;
    else if (key > root->key)
      node = node->right;
    else {
      m_info.current = node;
      return &node->data;
    }
  }
  return nullptr;
}

template <typename KEY_T, typename DATA_T>
auto CAvlTree<KEY_T, DATA_T>::AllocNode(void) -> CNode * {
  if (m_info.current = new CNode) {
    m_info.branchChanged = true;
    return m_info.current;
  }
  return nullptr;
}

并且使用 auto identifier ( argument-declarations... ) -> return_type 语法允许您直接使用 CName 名称。否则,您需要编写 template <typename KEY_T, typename DATA_T> typename CAvlTree<KEY_T, DATA_T>::CNode* CAvlTree<KEY_T, DATA_T>::AllocNode(void)