2011-04-17 23 views
11

Tengo un sistema de menú que quiero inicializar a partir de datos constantes. Un MenuItem puede contener, como un submenú, un vector de MenuItems. Pero solo funciona hasta cierto punto. Éstos son el esqueleto del problema:Inicializando una estructura que contiene un vector de sí mismo

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

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

g++ -std=c++0x (versión 4.4.5) hace frente a s1 y s2, pero s3 vuelve con:

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

(ver ideone). ¿Estoy haciendo algo mal?

+4

Es un comportamiento indefinido a tener un tipo incompleto como un argumento de plantilla a un contenedor de biblioteca estándar . – GManNickG

+5

Para obtener más información, vea Matt Austern's ["The Standard Librarian: Containers of Incomplete Types"] (http://drdobbs.com/184403814) –

Respuesta

11

GMan es correcto en su comentario: en la declaración de S::v en su código, S todavía está incompleto. Un tipo debe estar completo para ser utilizable como el tipo de valor en un contenedor STL. Para obtener más información, consulte el artículo de Matt Austern "The Standard Librarian: Containers of Incomplete Types."

Si tuviera que cambiar a un contenedor que se puede utilizar con un tipo incompleto, entonces su código es correcto. Por ejemplo, dada la siguiente:

#include <initializer_list> 

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

struct S { Container<S> v; }; 

entonces su inicialización original, debería funcionar bien:

S s3 = { { { } } } ; 

Esto funcionaría también:

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

+1 para ese artículo, es una buena lectura. – Xeo

+0

¿No es esto demasiado simplista hasta el absurdo? Eso no es "una estructura que contiene un vector de sí misma", y no has "cambiado a un contenedor que permite un tipo incompleto" ... ¡porque ninguno de ellos contiene ningún dato! Este código no almacena nada y no hace nada. Lo cual, me parece, es claramente paralelo a la imposibilidad de 'un objeto que se contiene a sí mismo' por valor. –

0

lo que usted está tratando de hacer es un próximo característica actual de C++ llamada "listas de inicialización", donde un vector o lista se puede inicializar con = {}. No sé si salieron con TR1 o no. tal vez está en 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; 
} 

el código que está utilizando no me parece adecuado. Si desea implementar estructuras que contienen estructuras (un árbol), incluya una lista de punteros a las estructuras/nodos (o simplemente punteros vacíos si eso no se puede implementar) dentro del nodo.

la mayoría de las estructuras de menú son esencialmente un árbol ordenado basado en listas (n nodos en un lugar, pero podrían ser m nodos en otro lugar, etc.). Robert Sedgewick hace un libro de texto "Algoritmos en 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; 
     } 
}; 

el problema es la dependencia mutua aquí, y creo que lo he resuelto con la declaración de la clase. hacer clase CTreeNode; antes de clase CTree {}. http://www.codeguru.com/forum/showthread.php?t=383253

Probablemente estoy destruyendo este código, y está incompleto, porque no he tenido la necesidad de escribir un árbol en años, pero creo que he cubierto lo básico. No implementé operador [].

+2

'std :: initialize_list <>' es una característica tanto de idioma como de biblioteca, y por lo tanto no puede formar parte de un informe técnico (es decir, es parte de C++ 0x solamente). – ildjarn

6

impulso :: opcional e impulsar :: recursive_wrapper mirar útil para este

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

Es necesario 4 llaves para cada submenú se agrega. La elisión de Brace no ocurre cuando las llamadas de constructor están involucradas.Por ejemplo

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

Honestamente, utilizando constructores se ven más legible

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

Ahora se simplifica a

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