2013-05-10 29 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可能不是。