在下面的代码中,我遍历一个map并测试是否需要擦除一个元素.擦除元素并继续迭代是否安全,或者我是否需要在另一个容器中收集密钥并执行第二个循环来调用erase()?
map<string, SerialdMsg::SerialFunction_t>::iterator pm_it;
for (pm_it = port_map.begin(); pm_it != port_map.end(); pm_it++)
{
if (pm_it->second == delete_this_id) {
port_map.erase(pm_it->first);
}
}
Run Code Online (Sandbox Code Playgroud)
有人可以帮帮我吗?
编译此代码:
void test()
{
std::set<int> test;
test.insert(42);
test.erase(std::remove(test.begin(), test.end(), 30), test.end()); // <- Line 33
}
Run Code Online (Sandbox Code Playgroud)
编译时生成以下错误:
$ make
g++ -c -Wall -pedantic-errors -Wextra -Wunused -Werror a_star.cpp
/usr/lib/gcc/i686-pc-cygwin/4.3.4/include/c++/bits/stl_algo.h: In function `_FIter std::remove(_FIter, _FIter, const _Tp&) [with _FIter = std::_Rb_tree_const_iterator<int>, _Tp = int]':
a_star.cpp:33: instantiated from here
/usr/lib/gcc/i686-pc-cygwin/4.3.4/include/c++/bits/stl_algo.h:779: error: assignment of read-only location `__result.std::_Rb_tree_const_iterator<_Tp>::operator* [with _Tp = int]()'
make: *** [a_star.o] Error 1
Run Code Online (Sandbox Code Playgroud) 根据这个非常高度推崇的答案,迭代一组擦除一些元素的规范方法如下:
for (it = mySet.begin(); it != mySet.end(); ) {
if (conditionToDelete(*it)) {
mySet.erase(it++);
}
else {
++it;
}
}
Run Code Online (Sandbox Code Playgroud)
当然,这是C++ 03设置擦除不返回迭代器的结果.否则一个人就可以写了it = mySet.erase(it);很明显,一个人可以写
itToDelete = it++;
mySet.erase(itToDelete);
Run Code Online (Sandbox Code Playgroud)
这个问题不是关于如何在迭代时删除元素.问题是为什么以下行显然不会导致未定义的行为.
mySet.erase(it++);
Run Code Online (Sandbox Code Playgroud)
起初我确信这必须是UB,因为我在考虑后增量方面做错了.这是一种常见(但错误的)方式,将预增量视为在评估的其余部分之前发生,并且后增量发生在AFTER之后.当然,这是错误的.后增量和前增量都有增加变量的副作用.不同之处在于这些表达式的价值.
也就是说,据我所知,C++标准(至少是C++ 03标准)没有明确说明何时会发生后增量的副作用.因此,除非我们保证如果作为后增量表达式的函数参数在进入函数体之前会产生副作用,那么这不应该是UB吗?究竟是什么(标准方面),如果有的话,禁止在迭代器在函数体内失效后发生的++副作用?
标准的行情非常受欢迎.
为了一个参数,让我们假设set的迭代器是一个内置类型,这实际上是operator ++,而不是重载的operator-function
I have two std::map<> objects a and b and would like to move (extract + insert) some elements (nodes) from one map to the other based on some predicate p.
for (auto i = a.begin(); i != a.end(); ++i)
if (p(*i))
b.insert(a.extract(i))
Run Code Online (Sandbox Code Playgroud)
This code segfaults in clang. I assume the problem is the increment of i after its node has been extracted from a.
Is the right/only way to fix this by using a post-increment?, E.g.:
for …Run Code Online (Sandbox Code Playgroud) 我已经读过这篇SO帖子了,这个也是关于std::set迭代过程中元素的擦除.但是,似乎C++ 17中存在一个更简单的解决方案:
#include <set>
#include <iostream>
int main(int argc,char **argv)
{
std::set<int> s;
s.insert(4);
s.insert(300);
s.insert(25);
s.insert(-8);
for (auto it:s)
{
if (it == -8)
{
s.erase(it);
}
}
std::cout << "s = {";
for (auto it:s)
{
std::cout << it << " ";
}
std::cout << "}\n";
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当我编译并运行它时,一切都很完美:
$ g++ -o main main.cpp
$ ./main
s = {4 25 300 }
Run Code Online (Sandbox Code Playgroud)
擦除这样的元素有什么警告吗?谢谢.
当我编译该程序时:
#include <list>
int main() {
std::list<int> l = {1, 2};
l.remove(l.front());
}
Run Code Online (Sandbox Code Playgroud)
使用ASAN和调试进行clang:
clang++-8 -fno-omit-frame-pointer -g -fsanitize=address -D_GLIBCXX_DEBUG -std=c++11 list-remove.cpp
Run Code Online (Sandbox Code Playgroud)
我得到了heap-use-after-free:
==31868==ERROR: AddressSanitizer: heap-use-after-free on address 0x603000000020 at pc 0x0000004fa1ae bp 0x7fff52cc5630 sp 0x7fff52cc5628
READ of size 4 at 0x603000000020 thread T0
#0 0x4fa1ad in std::__debug::list<int, std::allocator<int> >::remove(int const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/debug/list:649:18
#1 0x4f990f in main /tmp/list-remove.cpp:5:7
#2 0x7ff27d974b96 in __libc_start_main /build/glibc-OTsEL5/glibc-2.27/csu/../csu/libc-start.c:310
#3 0x41b879 in _start (/tmp/list-remove+0x41b879)
Run Code Online (Sandbox Code Playgroud)
似乎当removefinds x与第一个元素匹配时,它将从列表中删除该元素并将其删除。当检查第二个元素时,它将使用x已删除的元素来比较该元素。
根据C ++标准,这是正确的实现吗?最好先将元素移到末尾再删除它们。这样可以避免heap-use-after-free错误,但是也许不需要这样的实现。
从 …
考虑下面的代码
std::set<int> int_set = {1, 2, 3, 4};
for(const auto& key : int_set)
{
if(key == 2)
{
int_set.erase(key);
break;
}
}
Run Code Online (Sandbox Code Playgroud)
代码按预期运行,但是安全吗?
使用对键的引用来从集合中删除自身感觉是错误的,因为大概一旦发生删除,引用就不再有效。
具有相同潜在问题的另一个代码片段是
std::set<int> int_set = {1, 2, 3, 4};
const auto& key = *int_set.find(2);
int_set.erase(k);
Run Code Online (Sandbox Code Playgroud) 我最近更改了一些代码以使用集合而不是向量:
std::set<b2Body *>toDestroy;
//std::vector<b2Body *>toDestroy;
Run Code Online (Sandbox Code Playgroud)
但是现在我不确定如何迭代集合来查找对象.这就是我所拥有的:
std::vector<b2Body *>::iterator pos2;
for(pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2) {
b2Body *body = *pos2;
if (body->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *) body->GetUserData();
[self removeChild:sprite cleanup:YES];
}
_world->DestroyBody(body);
}
Run Code Online (Sandbox Code Playgroud)
现在toDestroy是一个集合的等价物是什么?来自Objective-C所以我只是学习C++的最佳实践.
编辑:添加我得到的错误消息:
error: no match for 'operator=' in 'pos2 = toDestroy. std::set<_Key, _Compare, _Alloc>::begin [with _Key = b2Body*, _Compare = std::less<b2Body*>, _Alloc = std::allocator<b2Body*>]()'
Run Code Online (Sandbox Code Playgroud) 我试图使用循环和迭代器从双端队列中删除一个元素.我正在关注在线示例,但看到了一个错误.
我正在使用g ++(GCC)4.8.3 20140911(Red Hat 4.8.3-9).
这是代码:
#include <iostream>
#include <deque>
using namespace std;
// Display the contents of a queue
void disp_deque(deque<int>& deque) {
cout << "deque contains: ";
for (auto itr = deque.begin(); itr!=deque.end(); ++itr)
cout << *itr << ' ';
cout << '\n';
}
int main(int argc, char** argv) {
deque<int> mydeque;
// Put 10 integers in the deque.
for (int i=1; i<=10; i++) mydeque.push_back(i);
disp_deque(mydeque);
auto it = mydeque.begin();
while (it!=mydeque.end()) {
cout << …Run Code Online (Sandbox Code Playgroud)