在 C++17 中迭代时从 std::set 中删除一个元素
Removing an element from a std::set while iterating over it in C++17
我已阅读 this SO post, and this one too 关于
在迭代期间从 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;
}
当我编译并运行一切顺利时:
$ g++ -o main main.cpp
$ ./main
s = {4 25 300 }
像这样擦除元素有什么注意事项吗?谢谢。
根据C++17标准:
9.5.4 The range-based for statement [stmt.ranged]
1 The range-based for statement
for ( for-range-declaration : for-range-initializer ) statement
is equivalent to
{
auto &&__range = for-range-initializer ;
auto __begin = begin-expr ;
auto __end = end-expr ;
for ( ; __begin != __end; ++__begin )
{
for-range-declaration = *__begin;
statement
}
}
所以 否 ,您的代码无效,因为您擦除迭代器当前指向的元素(std::set
同一个键只能有一个值!),因此迭代器失效并在 之后递增 ,这是未定义的行为。
请注意,您可以从集合中删除 另一个 元素,如 std::set
(以及 std::map
或 std::list
)只有被擦除的迭代器无效,而所有其他迭代器仍然有效。
如果你打算移除一个容器的当前元素(包括std::vector
,作为erase
returns一个新的,有效的迭代器),你需要回退到一个经典的循环,如参考问题的 answer 所示;我个人喜欢以下的单行变体:
iter = /*some condition*/ ? container.erase(iter) : std::next(iter);
如果您的 C++ 实现支持 Library Fundamentals TS(第二版),您可以:
#include <experimental/set>
#include <iostream>
#include <algorithm>
#include <experimental/iterator>
int main()
{
std::set<int> s;
s.insert(4);
s.insert(300);
s.insert(25);
s.insert(-8);
std::experimental::erase_if(s,
[](auto& key){ return key == -8; });
std::cout << "s = {";
std::copy(s.begin(), s.end(),
std::experimental::make_ostream_joiner(std::cout, " "));
std::cout << "}\n";
}
我已阅读 this SO post, and this one too 关于
在迭代期间从 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;
}
当我编译并运行一切顺利时:
$ g++ -o main main.cpp
$ ./main
s = {4 25 300 }
像这样擦除元素有什么注意事项吗?谢谢。
根据C++17标准:
9.5.4 The range-based for statement [stmt.ranged]
1 The range-based for statement
for ( for-range-declaration : for-range-initializer ) statement
is equivalent to
{ auto &&__range = for-range-initializer ; auto __begin = begin-expr ; auto __end = end-expr ; for ( ; __begin != __end; ++__begin ) { for-range-declaration = *__begin; statement } }
所以 否 ,您的代码无效,因为您擦除迭代器当前指向的元素(std::set
同一个键只能有一个值!),因此迭代器失效并在 之后递增 ,这是未定义的行为。
请注意,您可以从集合中删除 另一个 元素,如 std::set
(以及 std::map
或 std::list
)只有被擦除的迭代器无效,而所有其他迭代器仍然有效。
如果你打算移除一个容器的当前元素(包括std::vector
,作为erase
returns一个新的,有效的迭代器),你需要回退到一个经典的循环,如参考问题的 answer 所示;我个人喜欢以下的单行变体:
iter = /*some condition*/ ? container.erase(iter) : std::next(iter);
如果您的 C++ 实现支持 Library Fundamentals TS(第二版),您可以:
#include <experimental/set>
#include <iostream>
#include <algorithm>
#include <experimental/iterator>
int main()
{
std::set<int> s;
s.insert(4);
s.insert(300);
s.insert(25);
s.insert(-8);
std::experimental::erase_if(s,
[](auto& key){ return key == -8; });
std::cout << "s = {";
std::copy(s.begin(), s.end(),
std::experimental::make_ostream_joiner(std::cout, " "));
std::cout << "}\n";
}