[英]Does std::remove_if call the destructor?
我正在使用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。
std::remove_if
不动的元素被删除到最后。 取而代之的是,它的动作不被删除到前部元件; 尾部的内容未指定。
在此过程中,谓词所说的应该被删除的一些元素可能会被其他元素覆盖(确切地说,从其中分配)。 对于一个shared_ptr
的元素,这意味着它可以很好地破坏底层对象。
你似乎在寻找的是std::partition
。 它的行为与您期望std::remove_if
的行为方式完全相同。
这不是一个错误。 事实上,他们并没有被称为破坏者,他们只是被感动了。 在调用容器的erase
之前,不会调用它们的dtor。
通过移动(通过移动分配)范围中的元素来完成移除,使得不被移除的元素出现在范围的开头。 保留的元素的相对顺序被保留,容器的物理大小不变。 指向新逻辑结束和范围的物理结束之间的元素的迭代器仍然是可解除引用的,但元素本身具有未指定的值(根据MoveAssignable后置条件) 。 调用remove之后通常会调用容器的erase方法,该方法会擦除未指定的值并减小容器的物理大小以匹配其新的逻辑大小。
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
代码 [ 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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.