检查无符号整数没有索引的最佳实践
Best practice for checking for no index for an unsigned integer
假设我有这个功能:
void doThings(uint8_t index) {
if (an index is given) { ... }
}
通常,无效索引为-1,因此if 语句将是if (index != -1)
。如果我使用无符号整数来表示索引怎么办?将函数定义更改为带符号的 int 是否很愚蠢,以便我可以测试 -1
?对于无符号整数,是否有一个普遍接受的数字表示 'no index'?
简单地重载 doThings
,像这样:
void doThings(uint8_t index) {
// do things for a given index
}
void doThings() {
// do things for no index
}
或者,如果您只是传递函数的结果,比如 findElement
使用 std::pair
类似的东西:
std::pair<std::uint8_t, bool> findElement(...);
void doThings(std::pair<std::uint8_t, bool>& arg) {
if (arg.second) {
// do things for given element arg.first
并调用它:
doThings(findElement(...));
如果您必须考虑同一函数中的两种情况,更好的选择可能是只提供第二个参数。
void doThings(uint8_t index, bool indexGiven) {
if (indexGiven) { ... }
}
但是,使用两个完全不同的函数,一个用于指定索引,另一个用于不指定索引,可能会导致设计更简洁。
将函数定义更改为使用有符号整数并不愚蠢,这样您就可以检查 -1。这是一种常见的做法。
但是,如果该函数是其他人使用的定义明确且记录良好的 API 的一部分,那么您可能不希望将函数更改为使用有符号 int。相反,我建议使用 MAX_INT(或 uint8_t 的 0xFF)作为无效索引的标志。
我会选择 Maybe 类型。
// include some optional type, e.g. experimental/optional or boost/optional.hpp
using maybe_index = optional<std::uint8_t>;
void doThings(maybe_index index) {
if (index) { ... }
else { ... }
}
如果我需要能够表示一个索引加上一个特殊的无效状态。 GCC 在 std::experimental
中实现了提议的 std::optional
,并且有可用的 Boost 版本。
问问自己哪些值对索引有效。通常,您有一个数组,数组的长度定义了有效范围。如果该数组的长度恰好是 256 个元素,则可以使用 uint8_t
的每个可能值作为有效索引,这意味着您需要更多的元素来表示 "invalid index"。如果数组较小,则超出该数组范围的任何索引都是无效的,通常会使用最高值(即 static_cast<uint8_t>(-1)
或使用 <limits>
header 中的函数)。
这里已经有很多方法,例如一个额外的标志,使用 optional<uint8_t>
(你应该记住,因为它适用于你有类似要求的任何地方,而不仅仅是索引)或使用重载(这可能不是要走的路,因为这需要compile-time 决定)。
相反,我会使用更大的索引类型。用于表示索引的常用类型是 size_t
,这是一种无符号整数类型,通常具有指针大小(即普通计算机上的 32 位或 64 位)。如果你切换到那个,你将能够寻址甚至是内存中最大的数组(而不是在磁盘上!)。这样,您还可以使用 static_cast<size_t>(-1)
作为信号值来表示 "invalid index".
假设我有这个功能:
void doThings(uint8_t index) {
if (an index is given) { ... }
}
通常,无效索引为-1,因此if 语句将是if (index != -1)
。如果我使用无符号整数来表示索引怎么办?将函数定义更改为带符号的 int 是否很愚蠢,以便我可以测试 -1
?对于无符号整数,是否有一个普遍接受的数字表示 'no index'?
简单地重载 doThings
,像这样:
void doThings(uint8_t index) {
// do things for a given index
}
void doThings() {
// do things for no index
}
或者,如果您只是传递函数的结果,比如 findElement
使用 std::pair
类似的东西:
std::pair<std::uint8_t, bool> findElement(...);
void doThings(std::pair<std::uint8_t, bool>& arg) {
if (arg.second) {
// do things for given element arg.first
并调用它:
doThings(findElement(...));
如果您必须考虑同一函数中的两种情况,更好的选择可能是只提供第二个参数。
void doThings(uint8_t index, bool indexGiven) {
if (indexGiven) { ... }
}
但是,使用两个完全不同的函数,一个用于指定索引,另一个用于不指定索引,可能会导致设计更简洁。
将函数定义更改为使用有符号整数并不愚蠢,这样您就可以检查 -1。这是一种常见的做法。
但是,如果该函数是其他人使用的定义明确且记录良好的 API 的一部分,那么您可能不希望将函数更改为使用有符号 int。相反,我建议使用 MAX_INT(或 uint8_t 的 0xFF)作为无效索引的标志。
我会选择 Maybe 类型。
// include some optional type, e.g. experimental/optional or boost/optional.hpp
using maybe_index = optional<std::uint8_t>;
void doThings(maybe_index index) {
if (index) { ... }
else { ... }
}
如果我需要能够表示一个索引加上一个特殊的无效状态。 GCC 在 std::experimental
中实现了提议的 std::optional
,并且有可用的 Boost 版本。
问问自己哪些值对索引有效。通常,您有一个数组,数组的长度定义了有效范围。如果该数组的长度恰好是 256 个元素,则可以使用 uint8_t
的每个可能值作为有效索引,这意味着您需要更多的元素来表示 "invalid index"。如果数组较小,则超出该数组范围的任何索引都是无效的,通常会使用最高值(即 static_cast<uint8_t>(-1)
或使用 <limits>
header 中的函数)。
这里已经有很多方法,例如一个额外的标志,使用 optional<uint8_t>
(你应该记住,因为它适用于你有类似要求的任何地方,而不仅仅是索引)或使用重载(这可能不是要走的路,因为这需要compile-time 决定)。
相反,我会使用更大的索引类型。用于表示索引的常用类型是 size_t
,这是一种无符号整数类型,通常具有指针大小(即普通计算机上的 32 位或 64 位)。如果你切换到那个,你将能够寻址甚至是内存中最大的数组(而不是在磁盘上!)。这样,您还可以使用 static_cast<size_t>(-1)
作为信号值来表示 "invalid index".