Инициализация структуры, содержащей вектор

У меня есть система меню, которую я хочу инициализировать из постоянных данных. A MenuItem может содержать в качестве подменю вектор MenuItems. Но это работает только до определенной степени. Вот основные проблемы:

#include <vector>
struct S { std::vector<S> v ; } ;

S s1 = { } ;
S s2 = { { } } ;
S s3 = { { { } } } ;

g++ -std=c++0x (версия 4.4.5) справляется с s1 и s2, но s3 возвращается с:

prog.cpp:6:22: error: template argument 1 is invalid

(см. ideone). Я что-то делаю неправильно?

Ответ 1

GMan верен в своем комментарии: в объявлении S::v в вашем коде S все еще не завершен. Тип должен быть полным для использования в качестве типа значения в контейнере STL. Для получения дополнительной информации см. Статью Мэтта Остерна Стандартный библиотекарь: Контейнеры неполных типов.

Если вам нужно переключиться на контейнер, который можно использовать с неполным типом, то ваш код будет в порядке. Например, учитывая следующее:

#include <initializer_list>

template <typename T>
struct Container
{
    Container() { }
    Container(std::initializer_list<T>) { }
};

struct S { Container<S> v; };

то ваша оригинальная инициализация должна работать нормально:

S s3 = { { { } } } ;

Это тоже сработает:

S s4 = { { { { { { { { { { { { { { { { /*zomg*/ } } } } } } } } } } } } } } } };

Ответ 2

boost:: optional и boost:: recursive_wrapper выглядят полезными для этого

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

Вам нужно 4 фигурных скобки для каждого добавляемого вами подменю. Экранирование брекета не происходит, когда задействованы вызовы конструктора. Например

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

Честно говоря, использование конструкторов выглядит более читабельным

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; 
};

Теперь это упрощает

S s{ 
  "S1", 
  {
    "SS1",
    "SS2",
    { "SSS1", "SSS2" }
  }
};

Ответ 3

то, что вы пытаетесь сделать, - это текущая функция предстоящей на С++, называемая "списками инициализаторов", где вектор или список можно инициализировать с помощью = {}. Я не знаю, вышли ли они в TR1 или нет. возможно, это в 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;
}

используемый вами код не подходит мне. Если вы хотите реализовать структуры, содержащие структуры (дерево), включите список указателей на структуры/узлы (или просто указатели void, если они не реализованы) внутри node.

большинство структур меню по существу являются упорядоченным деревом на основе списка (n узлов в одном месте, но могут быть m узлами в другом месте и т.д.). Роберт Седжуик делает учебник "Алгоритмы в С++".

#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;
        }
};

проблема заключается в взаимной зависимости здесь, и я думаю, что я разрешил ее с объявлением класса. сделать класс CTreeNode; перед классом CTree {}. http://www.codeguru.com/forum/showthread.php?t=383253

Я, вероятно, искал этот код, и он неполный, потому что мне не нужно было писать дерево годами, но я думаю, что я осветил основы. Я не реализовал оператор [].