问题
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 delete
d the copy assignment operator, the move assignment operator is also implicitly delete
d.
Assuming Session
can support move semantics, you can get remove_if
to work by defining a move assignment operator. For instance, simply adding a default
ed 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 classSession
).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