最新版本的 Visual Studio C++ 编译器破坏了我们的代码。我们的应用程序使用 STL 算法,如测量样本上的 std::minmax_element。有时,设备不会提供有效的样本,而是会插入 NaN 元素。
到目前为止,STL 算法完全忽略了 NaN(GCC、CLANG、Visual Studio)
使用这个最小示例可以重现崩溃:
#include <algorithm>
#include <array>
#include <limits>
#include <iostream>
int main()
{
const double inf = std::numeric_limits<double>::infinity();
const double nan = std::numeric_limits<double>::quiet_NaN();
double values[] = {0, -inf, nan};
const auto mm = std::minmax_element(std::begin(values), std::end(values));
std::cout << "Min: " << *mm.first << std::endl;
std::cout << "Max: " << *mm.second << std::endl;
return std::distance(mm.first, mm.second);
}
游乐场:https://gcc.godbolt.org/z/GhTPsfj5q
预期:
example.cpp
ASM generation compiler returned: 0
example.cpp
Execution build compiler returned: 0
Program returned: 1
Min: -inf
Max: nan
VS 2022 >= 17.10
example.cpp
ASM generation compiler returned: 0
example.cpp
Execution build compiler returned: 0
Program returned: 4294967295
Min: -inf
Max: 0
MS 的 STL 专家认为 STL 算法需要“严格弱排序”。包含 NaN 的序列不满足此要求,因此我们在未定义行为的世界中徘徊。
我团队的一位 STL 专家了解该标准,因此只有在 C++-20 中使用 std::less 时才需要严格弱排序。
我仍然需要知道,在将来调用 STL 算法进行保存之前,我们是否必须预先清理数据序列。
谢谢你1000遍!
再见冈瑟
来自 C++17 最终草案:
28.7 排序及相关操作
- 28.7 中的所有操作都有两个版本:一种采用 Compare 类型的函数对象,另一种使用
。operator<
- Compare 是一个函数对象类型(23.14)。应用于 Compare 类型的对象的函数调用操作的返回值,当根据上下文转换为
(第 7 条)时,如果调用的第一个参数小于第二个参数,则产生bool
,否则产生true
。false
用于假设排序关系的算法。假设 comp 不会通过解引用迭代器应用任何非常量函数。Compare comp
- 对于所有采用
的算法,有一个版本使用Compare
代替。也就是说,operator<
默认为comp(*i, *j) != false
。 对于 28.7.3 中描述的算法以外的算法,*i < *j != false
应对值产生严格的弱排序。comp
28.7.3 列出了许多二分搜索算法(
lower_bound
、upper_bound
、equal_range
和 binary_search
),而 minmax_element
不在其中,因此它不能免除严格的弱排序要求,这意味着包括该范围内的 NaN
会使您的程序具有未定义的行为。