std :: remove_if是否会调用析构函数?

时间:2016-02-18 15:55:23

标签: c++

我使用std::remove_if将选定的std::vector元素移到最后,以便我可以将它们复制到另一个向量,然后删除范围:

using ElemPtr = std::shared_ptr<Elem>;
auto iter = std::remove_if(source.begin(),source.end(), [&](const ElemPtr& e){ /* ... */ });
dest.insert(dest.end(),iter,source.end());
source.erase(iter,source.end());

在调用std::remove_if之后,要删除的值会调用它们的析构函数(即它们被设置为null)。我最终只是复制了一堆空指针,这并不是很好。

像cppreference.com这样的资源没有提到这种行为,所以我想知道这是否是编译器错误?

我正在使用gcc 5.2.0。

3 个答案:

答案 0 :(得分:12)

std::remove_if 将要移除的元素移到最后。相反,它会将元素而不是移到前面;尾巴的内容没有说明。

在此过程中,谓词所说的应该删除的一些元素可能会被其他元素覆盖(确切地说,从其中分配)。对于一个shared_ptr的元素,这意味着它可以很好地破坏底层对象。

您似乎在寻找std::partition。它的行为与您期望std::remove_if的行为方式完全相同。

答案 1 :(得分:3)

这不是一个错误。事实上,他们并没有被称为破坏者,他们只是被感动了。在容器erase被调用之前,不会调用它们的dtor。

cppreference.com提到了,

  

通过移动(通过移动分配)范围内的元素来完成移除,使得不被移除的元素出现在范围的开头。保留的元素的相对顺序被保留,容器的物理大小不变。指向新逻辑结束和范围的物理结束之间的元素的迭代器仍然是可解除引用的,但元素本身具有未指定的值(根据MoveAssignable后置条件)。调用remove之后通常会调用容器的erase方法,该方法会擦除未指定的值并减小容器的物理大小以匹配其新的逻辑大小。

答案 2 :(得分:0)

std::remove_if移动将为迭代器值分配给定的一元谓词(如果它们最初出现在至少一个满足给定一元谓词的迭代器值之后)。没有析构函数将被调用。

std::remove_if的可能实现

template< typename ForwardIt, typename 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); // Move assignment, no destructors called
    return first;
}

来源cppreference.com

插图(Erase–remove idiom

代码 [Wandbox]:

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

struct A {
    constexpr A(int a) noexcept : m_a(a) {}
    constexpr A(const A&) noexcept = default;
    constexpr A(A&&) noexcept = default;
    ~A() noexcept {
        std::cout << "Destruction: " << m_a << std::endl;
    }
    A& operator=(const A& a) noexcept {
        std::cout << "Copy assignment: " << a.m_a << std::endl;
        m_a = a.m_a;
        return *this;
    }
    A& operator=(A&& a) noexcept {
        std::cout << "Move assignment: " << a.m_a << std::endl;
        m_a = a.m_a;
        return *this;
    }

    int m_a;
};


int main() {
    std::vector< A > v = { 1, 2, 5, 4, 5, 5 };
    std::cout << "BEGIN REMOVE" << std::endl;
    auto end = std::remove_if(v.begin(), v.end(), [](const A& a) noexcept { return 5 == a.m_a; });
    std::cout << "BEGIN ERASE" << std::endl;
    v.erase(end, v.end());
    std::cout << "END" << std::endl;
    return 0;
}

输出

Destruction: 5
Destruction: 5
Destruction: 4
Destruction: 5
Destruction: 2
Destruction: 1
BEGIN REMOVE
Move assignment: 4
BEGIN ERASE
Destruction: 4 // Could be garbage as well (unspecified, but valid, state)
Destruction: 5
Destruction: 5
END
Destruction: 1
Destruction: 2
Destruction: 4