如何使用C++20比较不同类型的整型值

描述

本篇介绍几个细琐的小特性,可以使代码更加安全可靠。

最常见的情况是采取 for loop 遍历某个容器,比如:

std::vector v(10);
std::iota(v, 0);
for (int i = v.size() - 1; i >= 0; --i) {
    std::cout << v[i] << ' ';
}

乍看之下,似乎并无问题,但实际上却存在安全隐患,若是 v.size() 的结果大于 std::numeric_limits::max(),将产生 UB。

倘若你使用了类型推导,问题会更加明显。

for (auto i = v.size() - 1; i >= 0; --i) {
    std::cout << v[i] << ' ';
}

这会输出超出预期的结果!i 被推导为 unsigned 整型,i >= 0 将永远为真。

这种隐患来自于类型的隐式转换,一般编译器只会给出警告。最简单的解决之法就是保证整型符号的一致性,例如:

for (size_t i = v.size() - 1; i < v.size(); --i) {
    std::cout << v[i] << ' ';
}

结束条件也随之变为检测数据范围,以避免条件在逻辑上的无效性。但如此一来,可读性直线降低,C++20 引入了几个与此相关的小特性,可以更安全地解决该问题。

第一个是一系列整型比较函数,它们可以安全地对不同符号的类型进行比较。如:

-1 > 0u; // true
std::cmp_greater(-1, 0u); // false

因此,可以用来安全地比较不同符号的整型。

for (int i = 0; std::cmp_less(i, v.size()); ++i) {
    std::cout << i << " " << v[i] << '
';
}

通过使用这些安全的比较函数,代码隐患随之消除。只是无法逆序遍历了,逆序时将 size_t 赋值到 int 依旧有可能产生 UB。

此种情境,更好的方式是采用 std::ssize(),它是一个有符号的 size() 辅助函数,表意更加直接。代码更改为:

for (int i = ssize(v) - 1; i >= 0; --i) {
    std::cout << v[i] << ' ';
}

得益于 ADL,std::ssize() 可以简写为 ssize()。

当然,以上只是示例需要,对于数据遍历,Range-based for loop 是更好的方式,这样能够避免很多易被忽视的错误。

for (const auto& elem : v) {
    std::cout << elem << ' ';
}

通过 C++20 Views,还可以在遍历时组合其他操作,如:

for (const auto& elem : v | std::reverse) {
    std::cout << elem << ' ';
}

 

这是可读性最强的方式。

当然,还有许多其他方法,比如迭代器、算法和一些技巧,但在范式上来说,那些方法很难比这里展示的方式更加简洁,就使用来说,记住这里提到的便已足够。

推荐阅读  点击标题可跳转

1、深入浅出 C++ 类型擦除

2、性能大杀器:c++中的copy elision

3、Configuring Transitive Dependencies with Modern CMake

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分