Use class name as argument to template class based class member?

69 views Asked by At
template < class _T >
class CList {
    private:
        class CNode {
            public:
                CNode * m_prev;
                CNode * m_next;
                _T      m_data;
            }; // CNode

    private:
        CNode   m_head;
        CNode   m_tail;
        CNode * m_headPtr;
        CNode * m_tailPtr;

    public: 
        CList () {
            m_headPtr = &m_head;
            m_tailPtr = &m_tail;
            m_headPtr->m_prev = 
            m_tailPtr->m_next = nullptr;
            m_headPtr->m_next = &m_tail;
            m_tailPtr->m_prev = &m_head;
        }       
        // ...
    }; // CList


class CArgValue;

class CArgValue {
    public:
        CList<CArgValue> m_subValues;

        CArgValue() {}
};

int main () {
    CArgValue v;
}

Error C2079 'CList<CArgValue>::CNode::m_data' uses undefined class 'CArgValue'  

Unfortunately, this doesn't work; I suppose because the definition of CArgValue is not complete when the compiler encounters "CList<CArgValue>". Is there a way to work around that?

Using Visual Studio 2019 community ed., c++20 enabled.

This is was gcc 10.1 (godbolt.org) says:

<source>: In instantiation of 'class CList<CArgValue>::CNode':
<source>:12:17:   required from 'class CList<CArgValue>'
<source>:34:26:   required from here
<source>:8:25: error: 'CList<_T>::CNode::m_data' has incomplete type
    8 |                 _T      m_data;
      |                         ^~~~~~
<source>:32:7: note: forward declaration of 'class CArgValue'
   32 | class CArgValue {
      |       ^~~~~~~~~
Compiler returned: 1
1

There are 1 answers

3
danadam On

If CArgValue has to be a concrete class then either m_data in CNode has to be a pointer or m_subValues has to be a pointer. Otherwise you end up with a situation that you have an object, which has a list, which has CArgValue head, which has a list, which has CArgValue head, which has ... You see the problem?

I don't know if that helps you, I didn't even analyze it enough to be sure it makes sense, but you could make CArgValue a template class too:

template<typename T>
class CArgValue {
public:
    CArgValue(T value) : m_value{std::move(value)} {}
private:
    CList<T> m_subValues;
    T m_value;
};

int main () {
    CArgValue<int> v{42};
}

This at least compiles, see godbolt.