在基于范围的循环与标准循环中擦除矢量元素
Erasing vector elements while in a range-based loop vs. standard loop
如果我在标准 for 循环的第一次迭代中删除向量的所有 5 个元素
std::vector<int> test {1, 2, 3, 4, 5};
for(int i = 0; i < test.size(); i++)
{
if(test[i] == 1) test.erase(test.begin(), test.end());
std::cout << i << " ";
}
它只会迭代一次,std::cout 输出将为“0”。
但是,如果我使用基于范围的循环做同样的事情,它将迭代 5 次,尽管 vector 的所有元素都被删除了。
int i = 0;
for (auto &a: test)
{
if (a==1) test.erase(test.begin(), test.end());
std::cout << i << " ";
i++;
}
并且 std::cout 输出将为“0 1 2 3 4”。
使用这两种类型的循环时,这种不同的行为从何而来?
在第一个情况下,每次迭代都会调用std::vector::size
函数。因此,如果您在第一次迭代中删除所有元素,则在第二次迭代开始之前调用的 std::vector::size
函数将 return 0。因此,第二次迭代不会发生,因为条件 i < test.size()
不满意
在second情况下,基于范围的for循环使用迭代器而不是std::vector::size
函数。当您调用 std::vector::erase
时,您会使所有迭代器失效,包括 end()
迭代器。因此,第二种情况实际上是 UB(未定义行为),你应该永远不要依赖它。
来自docs:
std::vector::erase
... Invalidates iterators and references at or after the point of the
erase, including the end() iterator.
根据标准,[stmt.ranged]/1
The range-based for statement
for ( init-statement opt for-range-declaration : for-range-initializer ) statement
is equivalent to
{
init-statement opt
auto &&range = for-range-initializer ;
auto begin = begin-expr ;
auto end = end-expr ;
for ( ; begin != end; ++begin ) {
for-range-declaration = * begin ;
statement
}
}
请注意,开始和结束迭代器是在循环开始之前初始化的。如果您在使迭代器无效的循环中执行擦除,则代码会导致 UB。
另一方面,第一个代码片段每次迭代都会检查 test.size()
。当元素被擦除并且 size()
变为 0
时,循环立即结束。
如果我在标准 for 循环的第一次迭代中删除向量的所有 5 个元素
std::vector<int> test {1, 2, 3, 4, 5};
for(int i = 0; i < test.size(); i++)
{
if(test[i] == 1) test.erase(test.begin(), test.end());
std::cout << i << " ";
}
它只会迭代一次,std::cout 输出将为“0”。
但是,如果我使用基于范围的循环做同样的事情,它将迭代 5 次,尽管 vector 的所有元素都被删除了。
int i = 0;
for (auto &a: test)
{
if (a==1) test.erase(test.begin(), test.end());
std::cout << i << " ";
i++;
}
并且 std::cout 输出将为“0 1 2 3 4”。
使用这两种类型的循环时,这种不同的行为从何而来?
在第一个情况下,每次迭代都会调用std::vector::size
函数。因此,如果您在第一次迭代中删除所有元素,则在第二次迭代开始之前调用的 std::vector::size
函数将 return 0。因此,第二次迭代不会发生,因为条件 i < test.size()
不满意
在second情况下,基于范围的for循环使用迭代器而不是std::vector::size
函数。当您调用 std::vector::erase
时,您会使所有迭代器失效,包括 end()
迭代器。因此,第二种情况实际上是 UB(未定义行为),你应该永远不要依赖它。
来自docs:
std::vector::erase
... Invalidates iterators and references at or after the point of the erase, including the end() iterator.
根据标准,[stmt.ranged]/1
The range-based for statement
for ( init-statement opt for-range-declaration : for-range-initializer ) statement
is equivalent to
{ init-statement opt auto &&range = for-range-initializer ; auto begin = begin-expr ; auto end = end-expr ; for ( ; begin != end; ++begin ) { for-range-declaration = * begin ; statement } }
请注意,开始和结束迭代器是在循环开始之前初始化的。如果您在使迭代器无效的循环中执行擦除,则代码会导致 UB。
另一方面,第一个代码片段每次迭代都会检查 test.size()
。当元素被擦除并且 size()
变为 0
时,循环立即结束。