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

发布于 2024-11-02 04:32:25 字数 504 浏览 1 评论 0原文

我有一个菜单系统,我想从常量数据初始化。 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)。我做错了什么吗?

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 up to a point. Here are the bare bones of the problem:

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

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

g++ -std=c++0x (version 4.4.5) copes with s1 and s2, but s3 comes back with:

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

(see ideone). Am I doing something wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

向日葵 2024-11-09 04:32:25

GMan 的评论是正确的:在代码中的 S::v 声明中,S 仍然不完整。类型必须完整才能用作 STL 容器中的值类型。 请参阅 Matt Austern 的文章“标准图书馆员:不完整类型的容器”。

有关详细信息, 如果容器可以与不完整的类型一起使用,那么您的代码就可以了。例如,给出以下内容:

#include <initializer_list>

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

struct S { Container<S> v; };

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

S s3 = { { { } } } ;

这也可以:

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

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*/ } } } } } } } } } } } } } } } };
晒暮凉 2024-11-09 04:32:25

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

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" }
  }
};
琉璃梦幻 2024-11-09 04:32:25

您想要做的是 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;
}

您使用的代码对我来说看起来不合适。如果要实现包含结构(树)的结构,请在节点内包含指向结构/节点的指针列表(如果不可实现,则仅包含空指针)。

大多数菜单结构本质上是一个基于列表的有序树(一个地方有 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

我可能正在修改这段代码,而且它是不完整的,因为我已经很多年不需要编写树了,但我认为我已经涵盖了基础知识。我没有实现运算符[]。

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[].

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文