为什么remove_if(.., lambda)表达式需要赋值运算符



我有这样的代码(简化):

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)。但是,由于您显式地 deleted了复制赋值操作符,因此移动赋值操作符也隐式地 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;
}

最新更新