g++下std::vector::erase返回值两种比较方式结果不一致问题
std::vector::erase返回值比较的跨平台差异分析
问题场景
在Ubuntu 20.04 AMD64环境下,使用g搭配libstdc并以-std=c++14编译C++代码时,出现了不符合预期的行为:
- 直接将
std::vector::erase的返回值在if语句中与v.cend()比较,结果为false - 先将
erase的返回值存储到变量中,再与v.cend()比较,结果为true
而在QNX ARM64环境下,使用QCC搭配libc++编译时,上述两段代码的比较结果均为true。这种差异的本质是未定义行为,以下是具体分析。
相关代码
返回false的代码
#include <vector> #include <iostream> #include <algorithm> int main() { std::vector<int> v{0, 1, 8, 3, 8, 5, 8, 7,8, 9}; int thing_id{9}; std::vector<int>::iterator const cit{ std::remove_if(v.begin(), v.end(), [thing_id](int const& thing) -> bool { return thing == thing_id; })}; if (v.erase(cit, v.cend()) == v.cend()) { std::cout << "true\n"; return true; } else { std::cout << "false\n"; return false; } }
返回true的代码
#include <vector> #include <iostream> #include <algorithm> int main() { std::vector<int> v{0, 1, 8, 3, 8, 5, 8, 7,8, 9}; int thing_id{9}; std::vector<int>::iterator const cit{ std::remove_if(v.begin(), v.end(), [thing_id](int const& thing) -> bool { return thing == thing_id; })}; auto const prev_end = v.erase(cit, v.cend()); if (prev_end == v.cend()) { std::cout << "true\n"; return true; } else { std::cout << "false\n"; return false; } }
原因解释
表达式求值顺序未定义
C++标准并未规定表达式中函数调用的执行顺序。在v.erase(cit, v.cend()) == v.cend()这个表达式里,编译器可以选择先求值右侧的v.cend(),再执行v.erase();也可以先执行v.erase(),再求值右侧的v.cend()。erase对容器的修改影响cend()的结果
std::vector::erase会修改容器的大小,调用后容器的尾迭代器(cend()返回的值)会发生变化:- 如果编译器先求值
v.cend(),此时得到的是修改前容器的尾迭代器;随后执行erase,返回的是修改后容器的尾迭代器,两者并不相等,因此比较结果为false。 - 而先存储
erase的返回值,再调用v.cend()时,cend()返回的是修改后容器的尾迭代器,和erase的返回值一致,因此比较结果为true。
- 如果编译器先求值
跨平台差异的本质是实现细节
libc的实现可能刚好保证了erase先执行,再求值cend(),因此两段代码都返回true;但这只是编译器/标准库的实现细节,并非C标准所要求的行为。这种依赖未定义行为的代码,在不同环境下出现差异是完全正常的。
内容的提问来源于stack exchange,提问作者mattsson
相关产品推荐
相关产品推荐

