0
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
Razzupaltuff
  • 2,250
  • 2
  • 21
  • 37
  • what is `CList` ? If it accepts an incomplete type then the code you posted is fine. – 463035818_is_not_an_ai Sep 30 '21 at 15:01
  • CList is a template class I wrote myself. It does not accept incomplete types. What would I need to do make it do so? – Razzupaltuff Sep 30 '21 at 15:05
  • 1
    after fixing the typos (missing `}` and `;`) I cannot reproduce the error: https://godbolt.org/z/e7d4jEjax. Well, in the meantime code has changed again... Please post a [mcve]. – 463035818_is_not_an_ai Sep 30 '21 at 15:12
  • https://godbolt.org/z/771jvj8ac – 463035818_is_not_an_ai Sep 30 '21 at 15:13
  • @NaN: Sorry, I was trying to create a working example that is as short as possible. I am done editing now. The "typos" were the result of my first posting incomplete code to show the principle. Afterwards I decided to turn it into complete code. – Razzupaltuff Sep 30 '21 at 15:14
  • I probably stripped the sample code down so much that your compiler optimized everything away. I will edit the example again to hopefully avoid that. – Razzupaltuff Sep 30 '21 at 15:22
  • `_T* m_data;` instead of `_T m_data;` would be fine with an incomplete `_T` – 463035818_is_not_an_ai Sep 30 '21 at 15:29
  • Thing is, I want to store the data in the list and not just a pointer to it. Allocating the data storage in the list is also not what I would want to do. – Razzupaltuff Sep 30 '21 at 15:31
  • i dont understand how the `CList` is supposed to work. It does store `m_head` and `m_tail`, but where are the other nodes stored? – 463035818_is_not_an_ai Sep 30 '21 at 15:34
  • It is incomplete code. The whole class is a full blown doubly linked list with various operations possible on it. You wanted a minimal executable example. That's what I presented (after some work on it). The CArgValue class should recursively build a tree of arguments and sub arguments. That's very easy to implement in Python, but a pain in the neck with C++ ... (I am porting a Python app as C++ coding exercise). – Razzupaltuff Sep 30 '21 at 15:41
  • the thing is, given the information you provided the answer is: no it isnt possible. `CArgValue` cannot have a member of type `CList` because `CArgValue` is not compelete – 463035818_is_not_an_ai Sep 30 '21 at 15:43
  • NaN: My question was whether there is a work around for it. Obviously there is with pointers, and danadam has proposed another solution. – Razzupaltuff Oct 03 '21 at 06:54

1 Answers1

0

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.

danadam
  • 3,350
  • 20
  • 18
  • Hi, I understand this. However, I believe it would be possible for a compiler to analyze the involved data structures / classes and fully resolve them (at least in my case) even if m_subValues is not a template class. I mean, it could do it for a template class, so it should well be able to do it for a concrete class. I might try the template class approach. Thank you very much. If your proposed solution works, I will upvote your reply and mark it is solution. – Razzupaltuff Oct 03 '21 at 06:53
  • I'm not sure I conveyed the point correctly. What I mean is that even if a class definition such as in your question was possible, then creating an object/instance of such class would require infinite amount of memory. Because you recursively need: a list that contains a head, that head contains a list, that list contains a head, etc. By using a pointer it is possible to break that chain. – danadam Oct 04 '21 at 06:28
  • Your code works because it doesn't reflect what I implemented. Your m_subValues is of type CList. However, it should be CList> - which doesn't work, I tried it ("incomplete type not allowed"). The only way to get what I want is to use pointers in the CList class. Thanks for your attempt to help though. – Razzupaltuff Oct 05 '21 at 23:06