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";
}
我的代码大致等同于以下所有意图和目的:
#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";
}