Initialising a struct that contains a vector of itself

前端 未结 3 1848
余生分开走
余生分开走 2020-12-19 01:00

I have a menu system that I want to initialise from constant data. A MenuItem can contain, as a sub-menu, a vector of MenuItems. But it only works

相关标签:
3条回答
  • boost::optional and boost::recursive_wrapper look useful for this

    struct S { // one brace
        boost::optional< // another brace
          boost::recursive_wrapper< // another brace
            std::vector< // another brace
              S
            >
          >
        > v; 
    };
    

    You need 4 braces for every submenu you add. Brace elision does not happen when constructor calls are involved. For example

    S m{{{{ 
      {{{{ }}}}, 
      {{{{ 
        {{{{ }}}}, 
        {{{{ }}}} 
      }}}} 
    }}}}; 
    

    Honestly, using constructors look more readable

    struct S {
        // this one is not really required by C++0x, but some GCC versions
        // require it.
        S(char const *s)
        :v(s) { } 
    
        S(std::string const& s)
        :v(s) { }
    
        S(std::initialize_list<S> s)
        :v(std::vector<S>(s)) { } 
    
        boost::variant<
          std::string,
          boost::recursive_wrapper<
            std::vector<
              S
            >
          >
        > v; 
    };
    

    Now it simplifies to

    S s{ 
      "S1", 
      {
        "SS1",
        "SS2",
        { "SSS1", "SSS2" }
      }
    };
    
    0 讨论(0)
  • 2020-12-19 01:59

    GMan is correct in his comment: in the declaration of S::v in your code, S is still incomplete. A type must be complete to be usable as the value type in an STL container. For more information see Matt Austern's article "The Standard Librarian: Containers of Incomplete Types."

    If you were to switch to a container that is usable with an incomplete type, then your code is fine. For example, given the following:

    #include <initializer_list>
    
    template <typename T>
    struct Container
    {
        Container() { }
        Container(std::initializer_list<T>) { }
    };
    
    struct S { Container<S> v; };
    

    then your original initialization should work fine:

    S s3 = { { { } } } ;
    

    This would work too:

    S s4 = { { { { { { { { { { { { { { { { /*zomg*/ } } } } } } } } } } } } } } } };
    
    0 讨论(0)
  • 2020-12-19 02:01

    what you are trying to do is an upcomingcurrent feature of C++ called "initializer lists", where a vector or list can be initialized with = { }. I don't know if they have come out with it in TR1 or not. maybe it's in TR2.

    #include <vector>
    #include <list>
    #include <iostream>
    using namespace std;
    int main(void) {
        vector<int> vi = {1, 2, 3, 4, 5};
        list<int> li = {5, 4, 3, 2, 1, 0};
        cout<<"vector size="<<vi.size()<<", list size="<<li.size()<<endl;
        return 0;
    }
    

    the code you are using doesn't look proper to me. If you want to implement structures that contain structures (a tree), include a list of pointers to the structures/nodes (or just void pointers if that's not implementable) within the node.

    most menu structures are essentially an ordered list-based tree (n nodes in one place, but could be m nodes elsewhere, etc). Robert Sedgewick makes a textbook "Algorithms in C++".

    #include <vector>
    #include <iterator>
    #include <string>
    void * pRoot = NULL; //pointer to CTree
    class CTreenode;
    class CTree;
    class CTree {
        public:
            vector<class CTreeNode> lctnNodeList; //list does not have at() or operator[]
            vector<class CTreeNode>::iterator lctni;
        public:
            CTree() {}
            ~CTree() {
                for (lctni=lctnNodeList.begin(); lctni != lctnNodeList.end(); nctni++) {
                    if (NULL==lctni->getChildPtr()) {
                        //do nothing, we have already done all we can
                    } else {
                        delete (CTree *)lctnNodeList.pChild;
                    }
                    //do action here
                }
            }
            void addToList(string& data, CTree * p) { 
                CTreeNode ctn(data, p);
                lctnNodeList.push_back(d); 
            }
            void eraseAt(size_t index) { 
                vector<class CTreeNode>::iterator i = lctnNodeList.begin();
                vector<class CTreeNode>::iterator i2 = lctnNodeList.begin();
                i2++;
                size_t x;
                for (x=0; x <= index; x++,i++,i2++) {
                    if (index == x) {
                        lctnNodeList.erase(i,i2);
                        break;
                    }
                }
            }
            void at(size_t index, string& returndata, CTree * &p) { 
                vector<class CTreeNode>::iterator i = lctnNodeList.begin();
                size_t x;
                for (x=0; x <= index; i++,x++) {
                    if (x==index) {
                         i->getData(returndata, p);
                         break;
                    }
                }
            }
            const CTreeNode& operator[](const size_t idx) {
                if (idx < lctnNodeList(size)) {
                    return lctnNodeList.at(idx);
                } else {
                    //throw an error
                }
            }
            const size() {
                return lctnNodeList.size();
            }
            //this can be applied to the root of the tree (pRoot).
            doActionToThisSubtree(void * root) {
                CTree * pct = (CTree *)root;
                for (pct->lctni=pct->lctnNodeList.begin(); pct->lctni != pct->lctnNodeList.end(); pct->nctni++) {
                    //this is a depth-first traversal.
                    if (NULL==pct->lctni->getChildPtr()) {
                        //do nothing, we have already done all we can
                        //we do this if statement to prevent infinite recursion
                    } else {
                        //at this point, recursively entering child domain
                        doActionToThisSubtree(pct->lctni->getChildPtr());
                        //at thisd point, exiting child domain
                    }
                    //do Action on CTreeNode node pct->lctni-> here.
                }
            }
    };
    class CTreeNode {
        public:
            CTree * pChild; //CTree *, may have to replace with void *
            string sData;
        public:
            CTreeNode() : pChild(NULL) {}
            CTreeNode(string& data, pchild) : pChild(pchild) {
                sData = data;
            }
            ~CTreeNode() { 
                 if (NULL!=pChild) { 
                     delete pChild;//delete (CTree *)pChild; 
                     pChild = NULL; 
                 }
            void getChild(CTreeNode& child) { 
                child = *pChild;//child = *((CTree *)pChild); 
            }
            bool addChild(string& s) { 
                if (NULL==pChild) {
                    return false;
                } else {
                    pChild = new CTree;
                }
                return true;
            }
            void * getChildPtr() { return pChild; }
            void getData(string& data, CTree * &p) { //not sure about how to put the & in there on CTree
                data=sData;
                p = pChild;
            }
            void setData(string& data, CTree * p) {
                sData=data;
                pChild = p;
            }
    };
    

    the problem is mutual dependency here, and I think I have it resolved with the class declaration. do class CTreeNode; before class CTree {}. http://www.codeguru.com/forum/showthread.php?t=383253

    I am probably mangling this code, and it's incomplete, because I haven't had the need to write a tree in years, but I think I've covered the basics. I didn't implement operator[].

    0 讨论(0)
提交回复
热议问题