我有这样的代码(简化):
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 ='
实际上,我在Session
类中删除了operator=
,如下所示:
Session& operator= (const Session& that) = delete;
我的问题是:为什么remove_if
使用lambda表达式需要赋值操作符?一个Session
对象在哪里赋值给另一个?
更新:正如@nosid和@Praetorian所解释的,remove_if
需要移动或复制构造函数&赋值运算符。根据c++ 11标准,move构造函数/赋值操作符应该由编译器自动生成。不幸的是,Visual Studio 2013没有做到这一点。由于类不可复制,remove_if
也不能进行复制,所以编译器会显示错误。我通过手动实现移动构造函数和移动赋值操作符来修复它。
std::remove_if
要求通过解引用迭代器获得的对象为MoveAssignable(§25.3.8/1)。但是,由于您显式地 delete
d了复制赋值操作符,因此移动赋值操作符也隐式地 CC_8d了。
假设Session
可以支持移动语义,您可以通过定义移动赋值操作符来使remove_if
工作。例如,简单地添加一个default
ed move赋值操作符就足以解决当前的问题(注意,您可能无法依赖编译器生成的版本,可能必须自己定义一个)。
Session& operator=(Session&&) = default;
现场演示
VS2013不支持默认的移动构造函数/赋值操作符,所以在你的情况下,你将被迫实现一个。
错误不是由lambda表达式引起的。如果将remove_if
替换为all_of
,可以看到lambda表达式没有问题:
// OK
std::all_of(_sessions.begin(), _sessions.end(),
[](const Session& session) { return false; });
错误是由std::remove_if
算法引起的。删除是通过移动范围内的元素来完成的,在这种方式下,不被删除的元素出现在范围的开始。因此,需要copy或move赋值。
对于其他需要复制或移动赋值操作的算法,即使它们没有使用lambda表达式,也会看到相同的错误消息。下面是一个例子:
// ERROR: Session& operator=(const Session&) is private
std::move(std::next(_sessions.begin()), _sessions.end(), _sessions.begin());
-
在
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;
}