这种递减的顺序是否调用了未定义的行为?
Is the sequencing of this decrement invoking undefined behaviour?
我正在寻找确认,澄清这段代码是否定义正确。
通过将迭代器重新分配给容器的 erase()
函数的结果来擦除循环中容器的元素是很常见的。循环通常有点乱,像这样:
struct peer { int i; peer(int i): i(i) {} };
int main()
{
std::list<peer> peers {0, 1, 2, 3, 4, 5, 6};
for(auto p = peers.begin(); p != peers.end();) // remember not to increment
{
if(p->i > 1 && p->i < 4)
p = peers.erase(p);
else
++p; // remember to increment
}
for(auto&& peer: peers)
std::cout << peer.i << ' ';
std::cout << '\n';
}
输出: 0 1 4 5 6
我想到以下内容可能更整洁,假设它没有调用未定义的行为:
struct peer { int i; peer(int i): i(i) {} };
int main()
{
std::list<peer> peers {0, 1, 2, 3, 4, 5, 6};
for(auto p = peers.begin(); p != peers.end(); ++p)
if(p->i > 1 && p->i < 4)
--(p = peers.erase(p)); // idiomatic decrement ???
for(auto&& peer: peers)
std::cout << peer.i << ' ';
std::cout << '\n';
}
输出: 0 1 4 5 6
我认为这有效的原因如下:
peers.erase()
将始终 return 递增 p
,因此再次递减它是安全的
peers.erase(p)
对 p
进行了 复制 ,因此它不会因顺序 reference 而对错误的值进行操作]
p = peers.erase(p)
returns a p&
所以递减操作在正确的对象上 reference.
但我有一些疑惑。我担心我通过在同一表达式中使用 --(p)
来调用错误的排序规则,其中 p
用作参数,尽管它在纸面上看起来没问题。
有人能看出我的评估有问题吗?还是定义明确?
这取决于您用来检测要删除的元素的条件。如果您尝试删除第一个元素,它将失败,因为 erase
将 return 新的 begin()
然后您正在递减它。 ,即使你立即再次增加它。
为了避免这个错误,并且因为它更常见和可读,我会坚持使用第一个版本。
如@DanielFrey 所述,第二个版本是错误的,但如果您不喜欢第一个版本,为什么不这样做:
std::list<int> myList = { 0, 1, 2, 3, 4, 5, 6 };
myList.remove_if(
[](int value) -> bool {
return value > 1 && value < 4;
}
);
/* even shorter version
myList.remove_if([](int value) -> bool {
return (value > 1 && value < 4);
});
*/
for(int value : myList) {
std::cout << value << " ";
}
这给出了输出:
0 1 4 5 6
我正在寻找确认,澄清这段代码是否定义正确。
通过将迭代器重新分配给容器的 erase()
函数的结果来擦除循环中容器的元素是很常见的。循环通常有点乱,像这样:
struct peer { int i; peer(int i): i(i) {} };
int main()
{
std::list<peer> peers {0, 1, 2, 3, 4, 5, 6};
for(auto p = peers.begin(); p != peers.end();) // remember not to increment
{
if(p->i > 1 && p->i < 4)
p = peers.erase(p);
else
++p; // remember to increment
}
for(auto&& peer: peers)
std::cout << peer.i << ' ';
std::cout << '\n';
}
输出: 0 1 4 5 6
我想到以下内容可能更整洁,假设它没有调用未定义的行为:
struct peer { int i; peer(int i): i(i) {} };
int main()
{
std::list<peer> peers {0, 1, 2, 3, 4, 5, 6};
for(auto p = peers.begin(); p != peers.end(); ++p)
if(p->i > 1 && p->i < 4)
--(p = peers.erase(p)); // idiomatic decrement ???
for(auto&& peer: peers)
std::cout << peer.i << ' ';
std::cout << '\n';
}
输出: 0 1 4 5 6
我认为这有效的原因如下:
peers.erase()
将始终 return 递增p
,因此再次递减它是安全的peers.erase(p)
对p
进行了 复制 ,因此它不会因顺序 reference 而对错误的值进行操作]p = peers.erase(p)
returns ap&
所以递减操作在正确的对象上 reference.
但我有一些疑惑。我担心我通过在同一表达式中使用 --(p)
来调用错误的排序规则,其中 p
用作参数,尽管它在纸面上看起来没问题。
有人能看出我的评估有问题吗?还是定义明确?
这取决于您用来检测要删除的元素的条件。如果您尝试删除第一个元素,它将失败,因为 erase
将 return 新的 begin()
然后您正在递减它。
为了避免这个错误,并且因为它更常见和可读,我会坚持使用第一个版本。
如@DanielFrey 所述,第二个版本是错误的,但如果您不喜欢第一个版本,为什么不这样做:
std::list<int> myList = { 0, 1, 2, 3, 4, 5, 6 };
myList.remove_if(
[](int value) -> bool {
return value > 1 && value < 4;
}
);
/* even shorter version
myList.remove_if([](int value) -> bool {
return (value > 1 && value < 4);
});
*/
for(int value : myList) {
std::cout << value << " ";
}
这给出了输出:
0 1 4 5 6