"矢量迭代器不兼容"

'vector iterators incompatible'

本文关键字:不兼容 迭代器      更新时间:2023-10-16

这个问题在SO上被问了很多次,但答案不适用于我的情况,AFAICT。以下代码在命中i != std::end(observers_);后立即触发错误。

void VisualGeometry::SignalPopPointFlags(const Point_2r& p,
                                         const uint32_t msec_delay) const {
    for(auto i = std::begin(observers_); i != std::end(observers_); ++i)
        (*i)->SlotPopPointFlags(p, msec_delay);
}

查看<vector>,以下触发错误:

void _Compat(const _Myiter& _Right) const
{   // test for compatible iterator pair
    if (this->_Getcont() == 0
        || this->_Getcont() != _Right._Getcont())
        {   // report error
            _DEBUG_ERROR("vector iterators incompatible");
            _SCL_SECURE_INVALID_ARGUMENT;
        }
}

由于我没有比较来自不同容器的迭代器,所以第一次检查this->_Getcont() == 0似乎是个问题,但我不知道如何判断。

如果我将begin(vec)/end(vec

我有点不知道这是怎么发生的。关于如何进行调试,有什么建议吗?

VisualGeometry类的设计目的是将它接收到的信号转发到任何正在观看它的对象

class VisualGeometry : public IGeometryObserver, public IObservableGeometry {
public:
    void SlotPushSegmentFlags(const Segment_2r& s, const uint32_t flags,
                              const uint32_t msec_delay = 0) override;
    void SlotPopSegmentFlags(const Segment_2r& s,
                             const uint32_t msec_delay = 0) override;
    void SignalPushSegmentFlags(const Segment_2r& s, const uint32_t flags,
                                const uint32_t msec_delay = 0) const override;
    void SignalPopSegmentFlags(const Segment_2r& s,
                               const uint32_t msec_delay = 0) const override;
    /* snip */    
private:
    std::vector<IGeometryObserver*> observers_;
};
void VisualGeometry::SlotPushSegmentFlags(const Segment_2r& s,
                                          const uint32_t flags,
                                          const uint32_t msec_delay) {
    SignalPushSegmentFlags(s, flags, msec_delay);
}
void VisualGeometry::SlotPopPointFlags(const Point_2r& p,
                                       const uint32_t msec_delay) {
    SignalPopPointFlags(p, msec_delay);
}
/* etc... */

首先要检查的是,在对vector进行迭代时,是否正在修改它。

看看这是否消除了问题:

void VisualGeometry::SignalPopPointFlags(const Point_2r& p,
                                         const uint32_t msec_delay) const {
  auto obs = observers_;
  for(auto i = std::begin(obs); i != std::end(obs); ++i)
    (*i)->SlotPopPointFlags(p, msec_delay);
}

处理这类问题很棘手,通常是你有设计问题的迹象。

通常,在调用一个回调序列时,如果该回调能够到达您正在迭代的序列并更改它或其成员,则需要添加一些生存期管理代码,并确定在当前发送一个回调时发送另一个回调意味着什么,以及在调用回调时安装或卸载回调意味着哪里。

一个简单的规则是,"如果在安装回调时安装了回调,则不会得到回调",但如果卸载了回调,就不会被调用。

为了产生这种效果,我的回调往往是weak_ptrs到std::functions的容器。当你安装回调时,你传递一个std::function,然后我将其复制到shared_ptr中。我从中派生出一个weak_ptr,并将其存储在回调容器中。

然后,我将shared_ptr返回给正在安装回调的代码。这个shared_ptr是生存期令牌:只要它(或它的副本)有效,我就会继续对它进行回调

在我的广播函数中,我首先扫描容器中过时的weak_ptr,然后将容器复制到本地std::vector<std::weak_ptr<std::function<void(message)>>>

然后,我对这个容器进行迭代,执行.lock()以获得std::shared_ptr<std::function<void(message)>>,然后如果它有效,则调用它

如果我在广播时触发了广播,它就会发生。(如果这种情况经常发生,我会大发雷霆,但那是另一个问题)。如果有人添加回拨,我很好。如果有人删除回调,我很好。

如果是多线程的,那么在本地std::vector上迭代时我不会被锁定,而是在清除无效的weak_ptr s、克隆weak_ptr s序列或向vector<weak_ptr<function<...>>>添加/删除回调时被锁定。