Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I get the error 'vector iterators incompatible'?

I am writing a small UI for my program. I have the method onMouseMotion(), which I can call in one of two ways (see code); if I call it through std::function, then != operator in the for loop stop condition produces the run-time exception vector iterators incompatible. Why?

class Widget : public EventHandler
{
protected:
    /* ... */
    std::vector<Widget *> children_;                        
    std::function<bool(Event &)> func_;

private:
    bool onMouseMotion(Event &event);
    /* ... */   
};

Widget::Widget() 
{
    /* ... */
    func_ = std::bind(&Widget::onMouseMotion, this, std::placeholders::_1);
    /* ... */
}

bool Widget::processEvent(Event &event)
{
    if (event.getType() == ui::EventType::MouseMotionEvent) {

        /* Method 1 - onMouseMotion works ok */     
        onMouseMotion(event);

        /* Method 2 - onMouseMotion throws */
        //func_(event);

        return true;
    }
}

bool Widget::onMouseMotion(Event &event)
{
    /* exception occurs on the next line, only when using Method 2 above */
    for (auto child = children_.rbegin(); child != children_.rend(); ++child) {}
}

Updates:

  • program is single-threaded.
  • the exception is thrown when entering the for loop, zero iterations occur.
  • compiling with MSVC.
  • same exception with an empty for loop.
  • rewritten examples to illustrate the std::function problem.
like image 550
Dan Nestor Avatar asked May 29 '14 09:05

Dan Nestor


People also ask

Does std :: move invalidate iterators?

For reference, std::vector::swap does not invalidate iterators.

Do you have to delete iterators?

Iterators are not owning (observing). They are "produced" for a container but are not actually the object that holds the memory. Hence you not only needn't, but also must not delete them.


1 Answers

So it is clear that

  • auto defines an iterator type for child, determined statically by the compiler (it cannot change between calls).
  • the type of child is assignment compatible with rbegin() and rend()
  • the type of child is relational operator compatible with rbegin() when called directly, but not when called through the bind() wrapper
  • since the type of child cannot change, the type of rend() must have, in the second case.

I see the following possibilities.

  1. The vector is a member and therefore the iterator type will be a pointer to member, and pointer to member has some restrictions on how it may be used.
  2. The value of this in the first case might be different from that captured in the bind (base class vs derived class, for example)
  3. The value of this may be delivered via a wrapper, which changes its type behaviour.

On balance, this is most likely to be an MSVC bug. The code as supplied does not compile and I am reluctant to try to modify and likely not be able to reproduce the bug. If you can post a repro case that compiles, I'd be happy to investigate further and update the answer.

like image 122
david.pfx Avatar answered Sep 27 '22 22:09

david.pfx