I know what I want, but I don't know how to tell the compiler what I want. I have split declarations and method definitions in .h and .cpp file, but the .cpp file gets included in the header, so all I did was separate declarations and definitions.
header file (avltree.h) - it includes the source file!:
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"
source file (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;
}
What the compiler says (VS 2019 community, c++2020 enabled):
E0276   name followed by '::' must be a class or namespace name
E0020   identifier "CNode" is undefined
E0864   CAvlTree is not a template
I have no bloody clue how to write this down the correct way. Please enlighten me.
I have similar code for a template class with a single typename which works, but cannot conclude from that how to do this for two typenames.
Using the tNodePtr type instead of CNode< KEY_T, DATA_T > * in my source code also doesn't work.
Btw, I know that the compiler doesn't "transfer" KEY_T and DATA_T from the CAvlTree declaration just because I am using the same names.
 
    