2014-06-06 79 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',這意味着允許拋出移動,並且它可以使用複製分配作爲後備。現在我明白了。 –

相關問題