检查无符号整数没有索引的最佳实践

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".