2014-06-06 48 views
2

我有这样的代码(简化):为什么remove_if(...,lambda)表达式需要赋值运算符?

std::vector<Session> sessions; 
// ... 
std::remove_if(sessions.begin(), sessions.end(), 
    [] (const Session& s) { 
     return false; 
    } 
); 

当我编译它(在Visual Studio 2013更新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; 

问题是:为什么那使用lambda表达式的remove_if需要赋值运算符?一个Session对象在哪里分配给另一个?

更新:正如@nosid解释和@Praetorian remove_if需要移动或拷贝构造函数&赋值运算符。根据C++ 11标准,移动构造函数/赋值运算符应该由编译器自动生成。不幸的是Visual Studio 2013 does not do that。由于该类不可复制remove_if也无法诉诸复制,因此编译器显示错误。我通过手动实现移动构造函数和移动赋值操作符来修复它。

+9

这不是_lambda表达式,需要'operator ='。它是'std :: remove_if'算法,因为它移动了元素(而不是删除它们)。 – nosid

+0

@nosid:听起来不错。你为什么不把它作为答案发布? –

+1

重写了一个触摸问题:虽然它完全不符合您的原始问题(毕竟问题不是您认为的问题),但对于有相同问题的人来说搜索应该更容易。 – Yakk

回答

5

该错误是不是由lambda表达式引起的。 Lambda表达式是好的,因为可以,如果你有all_of替换remove_if可以看出:

// 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
  • 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; 
} 
+0

'std :: remove_if'中没有_swaps_发生。 – nosid

+0

@nosid我不是指'std :: swap'我的意思是像你这样的转变:P – 101010

6

std::remove_if要求通过提领该迭代是MoveAssignable(§25.3.8/ 1)中获得的对象。但是因为您明确指定了复制赋值运算符,所以移动赋值运算符也隐含为delete d。

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

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

Live demo


VS2013不支持违约移动构造函数/赋值运算符,所以你的情况,你会被强制执行的。

+0

我认为这个要求比这个模糊一点,我认为它要求对象不能移动分配或者是复制 - 转让。这就是大多数容器无论如何工作,我假设'remove_if'也是如此。 –

+1

@MooingDuck你会这样想的,但是这就是所说的 - *要求:* * first的类型应满足MoveAssignable的要求*。 – Praetorian

+0

啊,它将'MoveAssignable'定义为't = rv',这意味着允许抛出移动,并且它可以使用复制分配作为后备。现在我明白了。 –

相关问题