4

这个问题已在 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) 换成 vec.begin()/vec.end(),也会出现同样的问题。

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

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... */
4

1 回答 1

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

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

一般来说,当调用一个回调序列时,如果该回调有任何方法可以到达您正在迭代的序列并更改它或其成员,您需要添加一些生命周期管理代码,并确定它对另一个回调意味着什么在当前发送时发送,以及在调用回调时安装或卸载回调意味着什么。

一个简单的规则是“如果您在安装回调时安装,则不会收到回调”,但如果您卸载回调,您将不会被调用。

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

然后我返回shared_ptr到安装回调的代码。这shared_ptr是生命周期令牌:只要它(或它的副本)有效,我将继续对其进行回调。

在我的广播功能中,我首先扫描我的容器以查找过时weak_ptr的 s,然后将容器复制到本地std::vector<std::weak_ptr<std::function<void(message)>>>.

然后我遍历这个容器,做 a.lock()来得到 a std::shared_ptr<std::function<void(message)>>,然后如果它是有效的调用它。

如果在我广播时触发了广播,它就会发生。(如果这种情况发生得太频繁,我会炸掉我的筹码,但这是另一个问题)。如果有人添加回调,我很好。如果有人删除回调,我很好。

如果事物是​​多线程的,则在迭代 local 时我不会被锁定std::vector,但在清除weak_ptr无效的weak_ptrs、克隆 s 的序列或添加/删除对vector<weak_ptr<function<...>>>.

于 2013-08-20T03:57:59.887 回答