初始化一个包含自身向量的结构
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)处理 s1
和 s2
,但 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<>
与库功能一样是语言功能,因此不能成为技术报告的一部分(即,仅是 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*/ ;
【讨论】:
这不是过于简单化到荒谬的地步了吗?那不是“包含自身向量的结构”,而且您还没有“切换到允许不完整类型的容器”……因为它们都不包含任何数据!此代码不存储任何内容,也不执行任何操作。在我看来,这与“一个包含自身的对象”的价值不可能性非常相似。以上是关于初始化一个包含自身向量的结构的主要内容,如果未能解决你的问题,请参考以下文章