我有这段代码(简化):
std::vector<Session> sessions;
// ...
std::remove_if( sessions.begin(), sessions.end(),
[] (const Session& s) {
return false;
}
);
当我编译它时(在Visual Studio 2013 Update 1中),我收到以下错误:
algorithm(1759): error C2280: 'Session &Session::operator =(const Session &)' : attempting to reference a deleted function
Session.h(78) : see declaration of 'Session::operator ='
确实,我已删除了operator=
类中的Session
,如下所示:
Session& operator= (const Session& that) = delete;
我的问题是:为什么remove_if
使用lambda表达式需要赋值运算符?一个Session
对象在哪里分配给另一个?
更新:正如@nosid和@Praetorian remove_if
所解释的,需要移动或复制构造函数&amp;赋值运算符。根据C ++ 11标准,移动构造函数/赋值运算符应该由编译器自动生成。不幸的是Visual Studio 2013 does not do that。由于类不可复制remove_if
也不能求助于复制,因此编译器会显示错误。我通过手动实现移动构造函数和移动赋值运算符来修复它。
答案 0 :(得分:6)
std::remove_if
要求通过解除引用迭代器获得的对象是MoveAssignable(第25.3.8 / 1节)。但是因为您明确delete
了复制赋值运算符,所以移动赋值运算符也隐含delete
d。
假设Session
可以支持移动语义,您可以通过定义移动赋值运算符来使remove_if
起作用。例如,简单地添加default
ed移动赋值运算符足以解决当前问题(请注意,您可能无法依赖编译器生成的版本,可能必须自己定义一个)。
Session& operator=(Session&&) = default;
VS2013不支持默认的移动构造函数/赋值运算符,因此在您的情况下,您将被迫实现一个。
答案 1 :(得分:5)
该错误不是由lambda表达式引起的。 lambda表达式很好,如果用remove_if
替换all_of
可以看到:
// OK
std::all_of(_sessions.begin(), _sessions.end(),
[](const Session& session) { return false; });
错误是由算法std::remove_if
引起的。通过移动范围内的元素来完成移除,使得不被移除的元素出现在范围的开头。因此,需要复制或移动分配。
对于需要复制或移动赋值操作的其他算法,您将看到相同的错误消息,即使它们在没有lambda表达式的情况下使用也是如此。这是一个例子:
// ERROR: Session& operator=(const Session&) is private
std::move(std::next(_sessions.begin()), _sessions.end(), _sessions.begin());
答案 2 :(得分:2)
在std::remove_if
中,您传递的范围元素之间会发生一些分配/移动。
似乎删除了Session
类的赋值运算符(即Session
个对象已被类Session
的实现者取消分配。
因此,编译器理所当然地抱怨它不能分配Session
类型的对象。
以下是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;
}