Why does remove_if( …, lambda ) expression require the assignment operator?

橙三吉。 提交于 2020-01-15 07:17:25

问题


I have this code (simplified) :

std::vector<Session> sessions;
// ...
std::remove_if( sessions.begin(), sessions.end(), 
   [] (const Session& s) {
      return false;
   }
);

When I compile it (in Visual Studio 2013 Update 1) I get the following error:

algorithm(1759): error C2280: 'Session &Session::operator =(const Session &)' : attempting to reference a deleted function
   Session.h(78) : see declaration of 'Session::operator ='

Indeed, I have deleted operator= in the Session class like this:

Session& operator= (const Session& that) = delete;

My question is: why does that remove_if using a lambda expression require the assignment operator? Where is one Session object assigned to another?

Update: As explained by @nosid and @Praetorian remove_if requires either move or copy constructor & assignment operator. According to the C++11 standard the move constructor/assignment operator should have been auto-generated by the compiler. Unfortunately Visual Studio 2013 does not do that. Since the class is not copyable remove_if cannot resort to copying, either, so the compiler displays the error. I fixed it by manually implementing a move constructor and a move assignment operator.


回答1:


The error is not caused by the lambda expression. The lambda expression is fine, as can be seen if you replace remove_if with all_of:

// OK
std::all_of(_sessions.begin(), _sessions.end(), 
    [](const Session& session) { return false; });

The error is caused by the algorithm std::remove_if. Removing is done by shifting the elements in the range in such a way that the elements that are not to be removed appear in the beginning of the range. For that reason, a copy or move assignment is required.

You will see the same error message for other algorithm that require copy or move assignment operations, even if they are used without a lambda expression. Here is an example:

// ERROR: Session& operator=(const Session&) is private
std::move(std::next(_sessions.begin()), _sessions.end(), _sessions.begin());



回答2:


std::remove_if requires that the objects obtained by dereferencing the iterator be MoveAssignable (§25.3.8/1). But because you've explicitly deleted the copy assignment operator, the move assignment operator is also implicitly deleted.

Assuming Session can support move semantics, you can get remove_if to work by defining a move assignment operator. For instance, simply adding a defaulted move assignment operator is enough to fix the immediate problem (note that you may not be able to depend on the compiler generated version and might have to define one yourself).

Session& operator=(Session&&) = default;

Live demo


VS2013 does not support defaulted move constructor/assignment operator, so in your case you'll be forced to implement one.




回答3:


  • In std::remove_if there're some assignments/moves happening between the elements of the range you pass as input.

  • It seems though, that the assignment operator of Session class is deleted (i.e., Session objects have been made un-assignable by the implementer of class Session).

  • Therefore, the compiler rightfully complains that it can't assign objects of type Session.


Below is a possible implementation of remove_if:

template<class ForwardIt, class UnaryPredicate>
ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p)
{
    first = std::find_if(first, last, p);
    if (first != last)
        for(ForwardIt i = first; ++i != last; )
            if (!p(*i))
                *first++ = std::move(*i); // COMPILER SAYS I CAN'T DO THIS!!!
    return first;
}


来源:https://stackoverflow.com/questions/24086717/why-does-remove-if-lambda-expression-require-the-assignment-operator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!