2013-05-10 19 views
4

reference由std :: remove_if去除的元素去哪裏?

template< class ForwardIt, class UnaryPredicate > 
ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p); 

迭代器指向老的 範圍內新的兩端之間的元素仍然提領,但元素本身具有 未定值。

我試過這個簡單的程序,找出他們的意思是「未指定的值」。

#include <vector> 
#include <memory> 
#include <iostream> 
#include <algorithm> 

int main() 
{ 
    std::vector< std::shared_ptr<int> > ints; 
    for (int i = 0; i < 10; ++i) 
     ints.push_back(std::make_shared<int>(i)); 
    std::remove_if(ints.begin(), ints.end(), 
        [](const std::shared_ptr<int>& element) 
        { 
         return *element % 7 != 0; 
        }); 
    for (int i = 0; i < 10; ++i) 
     std::cout << *ints[i] << std::endl; 
    return 0; 
} 

輸出是:

0 
7 
2 
3 
4 
5 
6 
The program has unexpectedly finished. 

這就是神祕的東西第七元素,這會導致段錯誤發生後的數據。

有趣的是,從here

template<class ForwardIt, class UnaryPredicate> 
ForwardIt remove_if(ForwardIt first, ForwardIt last, 
          UnaryPredicate p) 
{ 
    ForwardIt result = first; 
    for (; first != last; ++first) { 
     if (!p(*first)) { 
      *result++ = *first; 
     } 
    } 
    return result; 
} 

可能執行不產生段錯誤。

這是一個錯誤?由於迭代器應該是無法引用的。我正在使用gcc 4.7.3

回答

8

首先,如果你不知道,你需要在使用std::removestd::remove_if要記住很重要的事:他們實際上並不能抹去從底層容器元素。這意味着他們自己實際上並沒有刪除什麼。

你需要使用類似刪除/擦除成語:

auto to_erase = std::remove_if(ints.begin(), ints.end(), 
       [](const std::shared_ptr<int>& element) 
       { 
        return *element % 7 != 0; 
       }); 
ints.erase(to_erase, ints.end()); 

會發生什麼事「刪除」的元素是實現定義。下面是gcc實現:

template<typename _ForwardIterator, typename _Predicate> 
    _ForwardIterator 
    remove_if(_ForwardIterator __first, _ForwardIterator __last, 
      _Predicate __pred) 
    { 
     // concept requirements 
     __glibcxx_function_requires(_Mutable_ForwardIteratorConcept< 
        _ForwardIterator>) 
     __glibcxx_function_requires(_UnaryPredicateConcept<_Predicate, 
     typename iterator_traits<_ForwardIterator>::value_type>) 
     __glibcxx_requires_valid_range(__first, __last); 

     __first = _GLIBCXX_STD_A::find_if(__first, __last, __pred); 
     if(__first == __last) 
     return __first; 
     _ForwardIterator __result = __first; 
     ++__first; 
     for(; __first != __last; ++__first) 
     if(!bool(__pred(*__first))) 
      { 
      *__result = _GLIBCXX_MOVE(*__first); 
      ++__result; 
      } 
     return __result; 
    } 

很有可能是什麼原因造成的段錯誤是,這個實現調用_GLIBCXX_MOVE的事實。

+5

*「他們不能修改底層容器」*。那是不正確的。他們會修改容器,因爲操作會導致元素重新排列。他們不能做的是減少容器的大小(即'container.size()'會在'std :: remove_if'之前和之後返回相同的值,只有一些元素(被操作刪除)是未指定的*按照C++標準)。 – Nawaz 2013-05-10 07:01:49

+0

@Nawaz我的部分措辭不佳。我已經重新回答了我的答案。 – Yuushi 2013-05-10 07:04:38

+2

C++標準算法不適用於**容器**。它們適用於**序列**。容器是序列的一個來源,但不是唯一的來源。 – 2013-05-10 13:47:56

7

迭代器可能是可解引用的,但共享指針可能不可用。在取消引用具有未指定值的共享指針之前,應檢查是否爲空。

+0

但是,您認爲發生了什麼數據?如果實現如此簡單。在其實現中使用移動語義是否會導致這種行爲? – 2013-05-10 06:27:58

+3

我甚至沒有想過發生了什麼「數據」。共享指針的值不是合同的一部分,所以我根本不在乎它們。有可能範圍內的成員被移動,這會導致空的共享指針,或者可能是一個「老式」就地破壞和複製構造而不是賦值。你應該能夠在你的系統上找到'std :: remove_if'的源代碼(因爲它是一個模板)來驗證這一點。 – 2013-05-10 06:32:52

4

如果可能,C++ 11編譯器將使用移動語義移動未由std::remove_if「移除」的元素。移動shared_ptr會將原始shared_ptr對象留空(它不再擁有指針) - 在原始shared_ptr上調用get()將返回空指針。

因此,如果您取消引用shared_ptr,您將得到一個空指針取消引用。

所以,總之,雖然迭代器仍然可解引用,但shared_ptr可能不是。