我正在开发能够用C ++描述某些XML文件语义的数据结构。想法是检查各种元素的存在和/或正确顺序,同时将它们包含的文本存储到QHash对象中,并使用QString id(基于元素名称,但可自定义)作为键。
由于XML支持嵌套,我希望能够镜像这种嵌套。因此,每个XML元素都由“name”和(可选)“id”描述,这意味着它是最终的叶子,它的文本将被解析,或“名称”和其他元素描述符的列表,这意味着应该有这些当前的嵌套元素。
由于会有很多这样的语义方案,我想让描述其个别实例的代码非常紧凑。
我的想法是有一个类,它描述了一个元素,可以通过c ++ std :: initializer_list文字构建,我希望隐式支持嵌套。各种重载的构造函数可以在以后设置各种特定的细节。
我几乎到了那里,但现在卡住了。即使具有签名constructor(std::initializer_list<ProtocolDescriptorTestNode >);
的构造函数被正常地为每个嵌套的花括号调用,但是simillar签名构造函数constructor(QString, std::initializer_list<ProtocolDescriptorTestNode >);
从未被调用,即使我放置了像{{ {1}}
作为初始化文字。
请查看以下代码片段,与测试代码分开,并帮助我理解是否:
1。这是正常的c ++ 11行为,std :: initializer_list不支持这种嵌套与其他数据类型参数的组合。
2.这是gcc中实现的问题(bug;))(我使用的是版本4.9.1(Debian 4.9.1-1))
我在代码语法/语义中忽略了一些非常愚蠢的细节。
类声明(相关构造函数的摘录):
{ "xyz", { {"abc", "123"}, {"def","456"} } }
相关构造函数的定义:
class ProtocolDescriptorTestNode {
public:
ProtocolDescriptorTestNode(const ProtocolDescriptorTestNode&);
ProtocolDescriptorTestNode(std::initializer_list<ProtocolDescriptorTestNode > init); // 1
ProtocolDescriptorTestNode(QString name, std::initializer_list<ProtocolDescriptorTestNode > init); // 2
ProtocolDescriptorTestNode(QString name, QString id, enum eElementMode = modeDefault); // 4
ProtocolDescriptorTestNode(QString name, enum eElementMode = modeDefault); //5
~ProtocolDescriptorTestNode() {}
QString name, id;
tProtocolDescriptorTestList list;
};
测试对象实例: (注意:隐式/显式数据类型转换char * / QString没有区别)
ProtocolDescriptorTestNode::ProtocolDescriptorTestNode(std::initializer_list<ProtocolDescriptorTestNode> init)
{
qDebug() << "*** CONSTRUCTOR CALLED - 1 *** ";
qDebug() << init.size();
for(ProtocolDescriptorTestNode x : init) {
qDebug() << x.name << x.id;
}
}
ProtocolDescriptorTestNode::ProtocolDescriptorTestNode(QString name, std::initializer_list<ProtocolDescriptorTestNode> init) {
qDebug() << "*** CONSTRUCTOR CALLED - 2 *** ";
qDebug() << init.size();
for(ProtocolDescriptorTestNode x : init) {
qDebug() << x.name << x.id;
}
}
ProtocolDescriptorTestNode::ProtocolDescriptorTestNode(QString name, QString id, enum eElementMode) :
name(name),
id(id)
{
qDebug() << "*** CONSTRUCTOR CALLED - 4 *** ";
qDebug() << name << id;
}
ProtocolDescriptorTestNode::ProtocolDescriptorTestNode(QString name, enum eElementMode) :
name(name),
id("***")
{
qDebug() << "*** CONSTRUCTOR CALLED - 5 *** ";
qDebug() << name << id;
}
调试输出的相关部分,显示ProtocolDescriptorTestNode groupOther
({
{QString("name1"),"groupOther1"},
{"name2","groupOther2"},
{ QString("B"), {
{"name3","groupOther3"},
{
{"intra1","a"},
{QString("intra2")}
},
{"name4","groupOther4"}
} }
});
文字附近的初始化部分被视为"B"
和node(QString("B")
连接,而不是node(std::initializer_list)
,因为我的意图:
node(QString("B"), std::initializer_list)
答案 0 :(得分:0)
您遇到的问题是,如果某个类型的构造函数具有类型为initializer_list
的单个参数(或具有其他参数,但其余参数具有默认参数),则列表初始化将始终为比其他构造函数更喜欢构造函数(§13.3.1.7/ 1 [over.match.list] )。考虑到这一点,让我们按照初始化的方式工作。
在最外层,你有一个包含3个元素的braced-init-list:
{
{QString("name1"),"groupOther1"} // element 1
{"name2","groupOther2"} // element 2
{ QString("B"), ... } // element 3
}
ProtocolDescriptorTestNode
有一个构造函数,它接受initializer_list<ProtocolDescriptorTestNode>
类型的单个参数。由于我之前提到的规则,编译器将尝试将这3个元素中的每一个转换为ProtocolDescriptorTestNode
。
这些元素中的每一个本身都是braced-init-lists,因此首先尝试匹配initializer_list
构造函数。
考虑第一个元素:
{QString("name1"),"groupOther1"}
要将此转换为initializer_list<ProtocolDescriptorTestNode>
,第二个参数需要2个用户定义的转换,首先是QString
,然后是ProtocolDescriptorTestNode
,这是不允许的。所以考虑ProtocolDescriptorTestNode
的其他构造函数,它匹配构造函数4 。
请注意,如果第一个元素是
,行为会有所不同{QString("name1"),QString("groupOther1")}
在这种情况下,braced-init-list的每个元素都匹配构造函数5 以创建
ProtocolDescriptorTestNode
实例,然后这两个元素将形成initializer_list<ProtocolDescriptorTestNode>
和匹配构造函数1 。
下一个元素是
{"name2","groupOther2"}
再次匹配构造函数4 的原因与上一个案例相同。
第三个元素是
{ QString("B"),
{
{"name3","groupOther3"}, // constructor 4
{
{"intra1","a"}, // constructor 4
{QString("intra2")} // constructor 1
}, // constructor 1
{"name4","groupOther4"} // constructor 4
} // constructor 1
} // constructor 1
第一个子元素可以隐式转换为ProtocolDescriptorTestNode
(匹配构造函数5 ),第二个子元素本身也是一个braced-init-list,也可以转换为{ {1}}(匹配构造函数1 ),因为这个braced-init-list中的每个子元素本身都可以通过匹配各种构造函数隐式转换为ProtocolDescriptorTestNode
,如上面的评论。
因此,构造函数2 永远不会匹配。
经过长时间的解释之后,解决问题的方法非常简单,并在第一个元素的解释中暗示。 元素3 与ProtocolDescriptorTestNode
构造函数匹配的原因是因为它的两个子元素都可以隐式转换为initializer_list
。因此,请将ProtocolDescriptorTestNode
替换为QString("B")
。现在,它转换为"B"
需要两次用户定义的转换,ProtocolDescriptorTestNode
构造函数不再可行。将考虑其他构造函数,构造函数2 将匹配。