相当奇怪的叮当问题
Rather Strange clang issue
在写这篇文章之前,我尝试了几次谷歌搜索,但说实话,我不知道该搜索什么。我有一个C++项目,并且一直很高兴地使用GNU编译器(g++)。今天我尝试用 clang++ 编译并得到一个段错误。
好吧,好吧,我可以处理这个。在仔细阅读我的代码并打印一些东西后,我能够解决问题。然而,这个解决方案让我深感困扰和困惑。
情况是这样的:我正在使用一种树状数据结构来存储一个名为 Ligament 的类,但我将其存储在 std::vector 中。我通过存储"子"向量来做到这一点,这些向量实际上只是向量中父项和子项之间的整数偏移量。通过这种方式,我可以使用 this 指针访问儿童,即
child = this[offset];
然而,这些都不重要。这是这个问题:我有一个 Ligament::addChild(int) 函数,它接受一个整数并将其推送到作为 Ligament 成员的向量的背面:
void Ligament::addChild(uint32_t offset){
children.push_back(offset);
}
非常简单的东西。通常,我会向addChild传递一个从称为fill的递归函数返回的参数:
//starting at root
uint32_t fill(vector<Ligament>& lVec, TiXmlElement * el){
//store current size here, as size changes during recursion
uint32_t curIdx = lVec.size();
lVec.push_back(createLigament());
//Add all of this Ligament's children
TiXmlElement * i = el->FirstChildElement("drawable");
for (; i; i=i->NextSiblingElement("drawable")){
uint32_t tmp = fill(lVec, i) - curIdx;
lVec[curIdx].addChild(tmp);
//Does not work in clang++, but does in g++
//lVec[curIdx].addChild(fill(lVec,i)-curIdx);
}
//return the ligament's index
return curIdx;
}
fill 函数在 XML 元素上被调用,并首先遍历其子元素,深度优先。
抱歉,如果所有这些都不清楚,但问题的核心似乎是 for 循环中的内容。出于某种原因,我必须在将填充调用的返回值存储在变量中,然后再将其发送到 addChild 函数。
如果我不将其存储在临时变量中,似乎 addChild 函数不会更改子项的大小,但我无法想象为什么。
为了检查所有这些,我打印了这些调用前后儿童向量的大小,它从未超过 1。只有当我使用不是直接从函数返回的值调用 addChild 时,它才似乎有效。
在调用之前,我还打印出了 addChild 函数内部以及 for 循环内部的偏移量值。在所有情况下,这些值都是相同的,无论是在 clang++ 还是在 g++ 中。
由于问题已解决,我能够继续前进,但这是我希望能解决的问题。我做错了什么吗?
如果我能做更多的事情来使这个问题更清楚,请随意对我大喊大叫。
另外:我现在意识到通过这些递归通过引用传递 lVec 可能是不好的,因为push_back调用可能会导致地址更改。这是一个合理的担忧吗?
编辑:
因此,正如人们所指出的那样,我最后的担忧竟然与这个问题有关。fill 调用有可能调整向量的大小,而 lVec[curIdx] = 修饰符将更改向量中的元素。这些事情发生的顺序可能会产生严重的后果。
作为后续行动,使用 tmp 变量是否可以接受?仍然存在重新分配的问题...我想我会使用 SHR 的地图建议,然后在一切都说完后将其转换为矢量。
// Does not work in clang++, but does in g++:
lVec[curIdx].addChild(fill(lVec,i)-curIdx);
您看到的错误是由于依赖于评估顺序。由于fill(lVec, i)
可能会导致lVec
重新分配其元素,因此如果在fill(lVec,i)
之前评估lVec[curIdx]
,程序将具有未定义的行为。未指定函数参数的计算顺序 - 以及确定要调用哪个函数的后缀表达式。
我认为这是未定义的行为。
您推入 Vector,并在同一命令中更改它。
一个编译器可能首先执行fill
,另一个编译器可能先执行lVec[curIdx]
。
如果是这种情况,当您使用 map<uint32_t,uint32_t>
而不是 vector
时,它将适用于两个编译器。 因为 map 不需要内存是顺序的。
- 警告处理为错误这里有什么问题
- 最小硬币更换问题(自上而下方法)
- 为"adjacent"变量赋值时出现问题
- 我的神经网络不起作用 [XOR 问题]
- 在Ubuntu 16.04上安装Cilk时出现问题
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 编译包含字符串的代码时遇到问题
- Project Euler问题4的错误解决方案
- 问题:什么是QAbstractItemView::NoEditTriggers的反面
- 在编译C++代码(具有dlib和opencv)到WASM时面临问题
- 在进程中对同一管道进行读取和写入时C++管道出现问题
- 静态数据成员的问题-修复链接错误会导致编译器错误
- C++ 雷神库 - 使用资源加载器类时出现问题(不命名类型)
- 一个关于在C++中重载布尔运算符的问题
- 首要问题的答案让值班员搞错了
- setlocale的C++土耳其字符串问题
- 如何重构类层次结构以避免菱形问题
- 为什么在类似问题上通过叮当进行不同的诊断?
- 叮当的问题 --分析
- 相当奇怪的叮当问题