'矢量迭代器不兼容'

geo*_*erd 4 c++ iterator stl

这个问题在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);
}
Run Code Online (Sandbox Code Playgroud)

调查一下<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;
        }
}
Run Code Online (Sandbox Code Playgroud)

因为我没有比较来自不同容器的迭代器,所以似乎第一次检查this->_Getcont() == 0可能是问题,但我不知道该怎么说.

如果我为vec.begin()/ vec.end()换掉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... */
Run Code Online (Sandbox Code Playgroud)

Yak*_*ont 7

要检查的第一件事是看你是否正在修改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);
}
Run Code Online (Sandbox Code Playgroud)

处理这类问题很棘手,而且通常表明您遇到了设计问题.

通常,在调用一系列回调时,如果该回调有任何方法可以到达您正在迭代并更改它或其成员的序列,则需要添加一些生命周期管理代码,并确定它对另一个回调的含义当一个当前被发送时发送,以及在调用回调时安装或卸载回调的含义.

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

为了产生这种效果,我的回调往往是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_ptrs 的序列时,或者在添加/删除回调时被锁定vector<weak_ptr<function<...>>>.