【问题标题】:Splitting template class definitions and declarations fails for nested templates嵌套模板的拆分模板类定义和声明失败
【发布时间】:2021-11-21 12:42:02
【问题描述】:

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

头文件 (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

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

对于具有单个类型名的模板类,我有类似的代码,它可以工作,但无法从中得出如何为两个类型名执行此操作的结论。

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

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

【问题讨论】:

  • source file (avltree.cpp): 模板可疑,见why-can-templates-only-be-implemented-in-the-header-file
  • [Tangent] 你真的不应该把你的模板定义文件称为 cpp 文件。 cpp 文件需要编译,而你的不是。我称它为 tpp 或 ipp 文件来区分它。
  • 只是一个建议 - 不要调用实现文件 *.cpp - IDE/构建系统将尝试编译它们 - 它称之为 *.imp 或 *.inl 或类似的东西。
  • 编译器应该如何知道为CNode(在CNode* root; 和函数定义中的其他地方)选择哪些模板参数?您认为template &lt; class KEY_T, class DATA_T &gt; class CNode; 中的KEY_TDATA_T 应该是什么?
  • @t.niese 我也同意并重新打开了 Q。

标签: c++ template-classes


【解决方案1】:

你的问题是:

template < class KEY_T, class DATA_T >
class CNode;

那些KEY_TDATA_TCAvlTree 之一无关。

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

由于CNode 是一个类模板,编译器希望您在所有使用CNode 的地方为其提供模板参数(就像您为 CNode&lt; KEY_T, DATA_T&gt; * AllocNode(void); 所做的那样)。

但是,如果您在代码中为每个CNode 使用KEY_TDATA_TCAvlTree,那么问题是您为什么首先将CNode 定义为类模板。

而这部分代码表明你不希望CNode成为类模板:

template < class KEY_T, class DATA_T >
class CNode;

typedef CNode< KEY_T, DATA_T> * tNodePtr;

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

template &lt; class KEY_T, class DATA_T &gt; 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... ) -&gt; return_type 语法允许您直接使用CName 名称。否则,你需要写template &lt;typename KEY_T, typename DATA_T&gt; typename CAvlTree&lt;KEY_T, DATA_T&gt;::CNode* CAvlTree&lt;KEY_T, DATA_T&gt;::AllocNode(void)

【讨论】:

  • 伙计,我没想到不必让 CNode 成为模板类就可以在其中使用 KEY_T 和 DATA_T ......非常好的提示!当我看着它时,我情不自禁地面对自己......:D我显然让事情变得比我不得不做的更复杂。
  • 仍有一些问题我无法查明原因。您介意再看一下我的(编辑过的)问题吗?
  • @Razzupaltuff 不要以这种方式编辑原始问题。以这种方式更改代码会导致完全不同的问题,并将导致现有答案不再与问题匹配。如果您有新问题,请创建一个新问题。如果您认为这对读者有帮助,您可以在新的问题中参考这个问题。
  • @Razzupaltuff 关于您在该编辑中提出的问题,请查看我回答的最后一段。这是同样的问题。
  • 是的,我自己很快就想到了。谢谢您的帮助。 :)
猜你喜欢
  • 2019-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-30
相关资源
最近更新 更多