C++ 中具有不同符号的数量的算术和比较的最佳方法

Best approach for arithmetic and comparison of quantities with different signedness in C++

我的代码大致等同于以下所有意图和目的:

#include <vector>
#include <iostream>
#include <algorithm>

int main() {
  auto number = 2;
  auto vec = std::vector<int>{1, 2, 3, 4}; 
  auto number_location = std::find(vec.begin(), vec.end(), number);
  auto number_index = std::distance(vec.begin(), number_location);
  if (number_index + 3 >= vec.size()) {
    std::cout << "Too close to the end\n";
  }
  return 0;
}

其中number可以是任意整数。我相信,由于 std:vector 是一个随机访问容器,std::distance 可能会输出一个负值,因此 auto 推导出的值是有符号的,但是 对于这个特定的代码片段 ,无论 number 给出什么值,它都保证始终输出正值或零值。由于 number_index 已签名,因此在执行比较时 number_index + 2 > vec.size() 我的编译器会发出警告(我正在使用 g++ 9.3 编译它,并带有标志 -Wall -Wextra -Wpedantic -std=c++17):

main.cpp: In function ‘int main()’:
main.cpp:10:24: warning: comparison of integer expressions of different signedness: ‘long int’ and ‘std::vector<int>::size_type’ {aka ‘long unsigned int’} [-Wsign-compare]
   10 |   if (number_index + 3 >= vec.size()) {
      |       ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~

我的问题是:进行静态转换是否是一种合理的方法,可能是 static_cast<long>(vec.size())static_cast<std::vector<int>::size_type>(number_index),或者是否有更好的方法来处理这样的代码,其中 unsigned 之间的距离弹出“通常可以签名但在感兴趣的情况下未签名”的值?

对于这种情况,我建议在比较语句之前先使用带符号的整型变量来捕获大小。

long size = vec.size();
if (number_index + 3 >= size) {

如果您希望向量的大小太大而无法放入long,您可以使用std::vector::size_type对应的带符号类型使其更健壮。

std::make_signed_t<std::vector<int>::size_type> size = vec.size();
if (number_index + 3 >= size) {

std::ssize(vec) 自 C++20

起可用

如果您确定(正式)signed变量的值不会为负,那么避免警告的最安全方法是将其转换为 size_t 类型,因为无符号类型的最大值高于相同大小的有符号类型。此外,在大多数平台上,ptrdiff_t(您的 number_index 的推导类型)将与 size_t 类型的大小相同。

将无符号值转换为有符号类型(相同大小)不太安全,因为存在溢出的可能性。

因此,这可能是一个安全的转换:

    if (static_cast<size_t>(number_index) + 3 >= vec.size()) {
        std::cout << "Too close to the end\n";
    }

如果目标是将位置与大小进行比较,并且在不使用强制转换的情况下避免此警告,那么一种方法是始终使用相同的 distance 指标来确定大小。

auto size = std::distance(vec.begin(), vec.end());
if (number_index + 3 >= size) {
  std::cout << "Too close to the end\n";
}

但是,如果可以的话,您可能希望修改比较逻辑以直接使用迭代器而不是使用大小。在这种情况下,这样做可以更好地传达打印消息的意图。

if (vec.end() - number_location <= 3) {
  std::cout << "Too close to the end\n";
}