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

时间:2014-06-06 16:33:22

标签: c++ c++11 stl lambda operator-overloading

我有这段代码(简化):

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也不能求助于复制,因此编译器会显示错误。我通过手动实现移动构造函数和移动赋值运算符来修复它。

3 个答案:

答案 0 :(得分:6)

std::remove_if 要求通过解除引用迭代器获得的对象是MoveAssignable(第25.3.8 / 1节)。但是因为您明确delete了复制赋值运算符,所以移动赋值运算符也隐含delete d。

假设Session可以支持移动语义,您可以通过定义移动赋值运算符来使remove_if起作用。例如,简单地添加default ed移动赋值运算符足以解决当前问题(请注意,您可能无法依赖编译器生成的版本,可能必须自己定义一个)。

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

Live demo


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;
}