gpt4 book ai didi

c++ - 嵌套列表初始化与将 initializer_list 作为第二个参数的构造函数不匹配

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:53:00 27 4
gpt4 key购买 nike

我正在开发能够用 C++ 描述某些 XML 文件语义的数据结构。想法是检查各种元素的存在和/或正确顺序,同时将它们包含的文本存储到 QHash 对象中,使用 QString id(基于元素名称,但可自定义)作为键。

由于 XML 支持嵌套,我希望能够镜像这种嵌套。所以每个 XML 元素要么由“名称”和(可选)“id”描述,这意味着它是最后的叶子并且它的文本将被解析,或者“名称”和其他元素描述符的列表,这意味着应该有这些当前元素中的嵌套元素。

由于会有很多这样的语义方案,我希望描述它们的各个实例的代码非常紧凑。

我的想法是有一个类,它描述一个元素并且可以通过 c++ std::initializer_list 文字构造,我希望支持隐式嵌套。各种重载的构造函数,稍后可以设置各种特定的细节。

我快到了,但现在卡住了。尽管带有签名 constructor(std::initializer_list<ProtocolDescriptorTestNode >);
的构造函数通常为每个嵌套花括号调用,但类似的签名构造函数 constructor(QString, std::initializer_list<ProtocolDescriptorTestNode >);
从未被调用,即使我放置了像
{ "xyz", { {"abc", "123"}, {"def","456"} } } 这样的构造
作为初始化文字。

请查看以下与测试代码分开的代码片段,并帮助我理解:
1。这是正常的 c++11 行为,std::initializer_list 不支持这种与其他数据类型参数结合的嵌套。
2. 这是 gcc 中的实现 (bug ;)) 问题(我使用的是版本 4.9.1 (Debian 4.9.1-1))
3. 我忽略了代码语法/语义中一些非常愚蠢的细节。

类声明(相关构造函数摘录):

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

相关构造函数的定义:

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

测试对象实例:(注意:隐式/显式数据类型转换 char */QString 没有区别)

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):

*** CONSTRUCTOR CALLED - 4 *** 
"name1" "groupOther1"
*** CONSTRUCTOR CALLED - 4 ***
"name2" "groupOther2"
*** CONSTRUCTOR CALLED - 5 ***
"B" "***"
*** CONSTRUCTOR CALLED - 4 ***
"name3" "groupOther3"
*** CONSTRUCTOR CALLED - 4 ***
"intra1" "a"
*** CONSTRUCTOR CALLED - 5 ***
"intra2" "***"
*** CONSTRUCTOR CALLED - 1 ***
1
"intra2" "***"
*** CONSTRUCTOR CALLED - 1 ***
2
"intra1" "a"
"" ""
*** CONSTRUCTOR CALLED - 4 ***
"name4" "groupOther4"
*** CONSTRUCTOR CALLED - 1 ***
3
"name3" "groupOther3"
"" ""
"name4" "groupOther4"
*** CONSTRUCTOR CALLED - 1 ***
2
"B" "***"
"" ""
*** CONSTRUCTOR CALLED - 1 ***
3
"name1" "groupOther1"
"name2" "groupOther2"
"" ""

最佳答案

你遇到的问题是,如果一个类型有一个构造函数,其参数类型为 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。 .

这些元素中的每一个本身都是花括号初始化列表,因此将尝试匹配 initializer_list构造函数首先。


考虑第一个元素:

{QString("name1"),"groupOther1"}

将其转换为 initializer_list<ProtocolDescriptorTestNode> ,第二个参数需要 2 个用户定义的转换,首先是 QString然后到ProtocolDescriptorTestNode , 这是不允许的。所以 ProtocolDescriptorTestNode 的其他构造函数被考虑,它匹配 构造函数 4

Note that behavior would be different if the first element had been

 {QString("name1"),QString("groupOther1")}

In this case, each element of the braced-init-list would match constructor 5 to create a ProtocolDescriptorTestNode instance, and the two would then form an initializer_list<ProtocolDescriptorTestNode> and match constructor 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 (匹配 constructor 5),第二个子元素本身是一个 braced-init-list,也可以转换为 ProtocolDescriptorTestNode。 (匹配 constructor 1),因为这个 braced-init-list 中的每个子元素本身都可以隐式转换为 ProtocolDescriptorTestNode通过匹配各种构造函数,如上面的评论所示。

因此构造函数 2 永远不会匹配。


在冗长的解释之后,您的问题的解决出奇地简单,并且在对第一个元素的解释中有所暗示。 元素 3 匹配 initializer_list 的原因constructor 是因为它的两个子元素都可以隐式转换为 ProtocolDescriptorTestNode .所以,替换 QString("B")"B" .现在它转换为 ProtocolDescriptorTestNode需要两个用户定义的转换,并且 initializer_list构造函数不再可行。会考虑其他构造函数,匹配构造函数2

Live demo

关于c++ - 嵌套列表初始化与将 initializer_list 作为第二个参数的构造函数不匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25071206/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com