不可能的事件顺序

Impossible sequence of events

本文关键字:顺序 事件 不可能      更新时间:2023-10-16

我试图在for循环中跟踪一个神秘的迭代器问题。我在迭代器的operator!=中得到一个错误,这通常意味着正在比较的迭代器不属于同一个容器。跟踪到微软库的实现,operator!=调用operator==,其中测试为真:

    bool operator==(const _Myiter& _Right) const
        {   // test for iterator equality
 #if _ITERATOR_DEBUG_LEVEL == 2
        if (this->_Getcont() == 0
            || this->_Getcont() != _Right._Getcont())
            {   // report error
            _DEBUG_ERROR("list iterators incompatible");

为了获得更多的信息,我写了这个小函数来替换for循环中的!=:

template<typename iter>
bool bang_equal(const iter & left, const iter & right)
{
   static int count = 0;
   auto p1 = left._Getcont();
   auto p2 = right._Getcont();
   ATLTRACE("Iterator comparison left _Getcont()=%p right _Getcont()=%p %dn", p1, p2, ++count);
   MemoryBarrier();
   bool b = left != right;
   MemoryBarrier();
   auto p3 = left._Getcont();
   auto p4 = right._Getcont();
   ATLTRACE("                    left _Getcont()=%p right _Getcont()=%p %dn", p3, p4, ++count);
   return b;
}

这就是有趣的地方。我仍然得到表达式left != right中的错误,调试器停止在那里,但要么第一个ATLTRACE已被跳过,要么第二个已提前运行!调试器的输出有两行,并且调试器显示的count的值与输出的最后一行匹配。

Iterator comparison left _Getcont()=07D0B2C8 right _Getcont()=07D0B2C8 2984
                    left _Getcont()=07D0B2C8 right _Getcont()=07D0B2C8 2985
Myprog.exe has triggered a breakpoint.

查看反汇编窗口,可以看到按照预期顺序的指令。我难住了。可能会发生什么?

终于想通了。Microsoft函数_Debug_message显示一个对话框,询问您是要中止、重试(调试)还是忽略错误。在显示对话框时,消息泵仍在运行,从而允许进行其他活动。我的函数再次被调用,这一次它运行到完成,在这个过程中生成了大量的调试输出。如果我在库代码的_DEBUG_ERROR行上设置一个显式断点,我就可以捕获错误,而无需在后台执行额外的执行。事后回顾调试输出,我可以看到预期的错误输出确实在那里,只是隐藏得太深了,我从来没有看到过它。

我的直觉告诉我,Occam剃刀是这里最可能的解释:特别是在迭代期间使迭代器无效。在for循环中没有++iter这一事实进一步强调了它不是对每个元素进行直接迭代。

它可能不是直接在循环中,但容器可能在调用链中的某个地方被别名从循环体调用——这些是非常容易犯的错误,诊断起来绝对是残酷的。至少应该在每次迭代时打印容器的大小。

如果你有Linux操作系统,并且有一小部分代码重现了这个问题,你可以使用valgrind来帮助你找到这个问题