初始化一个包含自身向量的结构

Posted

技术标签:

【中文标题】初始化一个包含自身向量的结构【英文标题】:Initialising a struct that contains a vector of itself 【发布时间】:2011-08-07 04:44:42 【问题描述】:

我有一个菜单系统,我想从常量数据中初始化。 MenuItem 可以包含MenuItems 的向量作为子菜单。但它只能在一定程度上起作用。以下是问题的核心:

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

S s1 =   ;
S s2 =     ;
S s3 =       ;

g++ -std=c++0x(版本 4.4.5)处理 s1s2,但 s3 回来了:

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

(请参阅ideone)。我做错了吗?

【问题讨论】:

将不完整的类型作为标准库容器的模板参数是未定义的行为。 欲了解更多信息,请参阅 Matt Austern 的"The Standard Librarian: Containers of Incomplete Types" 【参考方案1】:

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

【讨论】:

【参考方案2】:

您正在尝试做的是一个即将推出当前 C++ 的功能,称为“初始化列表”,其中可以使用 = 初始化向量或列表。 我不知道他们是否在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 指针)。

大多数菜单结构本质上是一个有序的基于列表的树(一个地方有 n 个节点,但其他地方可能是 m 个节点,等等)。 Robert Sedgewick 编写了一本教科书“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;
        
;

这里的问题是相互依赖,我想我已经用类声明解决了。 做类 CTreeNode;在 CTree 类之前。 http://www.codeguru.com/forum/showthread.php?t=383253

我可能正在修改这段代码,而且它不完整,因为我已经好几年没有需要写一棵树了,但我想我已经涵盖了基础知识。我没有实现 operator[]。

【讨论】:

std::initialize_list&lt;&gt; 与库功能一样是语言功能,因此不能成为技术报告的一部分(即,仅是 C++0x 的一部分)。【参考方案3】:

GMan 在他的评论中是正确的:在您的代码中 S::v 的声明中,S 仍然不完整。类型必须完整才能用作 STL 容器中的值类型。更多信息请参阅 Matt Austern 的文章"The Standard Librarian: Containers of Incomplete Types."

如果您要切换到可用于不完整类型的容器,那么您的代码就可以了。例如,给定以下内容:

#include <initializer_list>

template <typename T>
struct Container

    Container()  
    Container(std::initializer_list<T>)  
;

struct S  Container<S> v; ;

那么您的原始初始化应该可以正常工作:

S s3 =       ;

这也可以:

S s4 =                 /*zomg*/                ;

【讨论】:

这不是过于简单化到荒谬的地步了吗?那不是“包含自身向量的结构”,而且您还没有“切换到允许不完整类型的容器”……因为它们都不包含任何数据!此代码不存储任何内容,也不执行任何操作。在我看来,这与“一个包含自身的对象”的价值不可能性非常相似。

以上是关于初始化一个包含自身向量的结构的主要内容,如果未能解决你的问题,请参考以下文章

尝试初始化结构向量时出现编译错误

初始化结构向量向量的好方法(每个都有一个初始值)?

如何初始化包含 opencv:matrices 的二维向量?

FastText

小白学习C++ 教程五C++数据结构向量和数组

c ++初始化结构向量的正确方法