将查找表/索引到数组中的数据类型

Datatype for lookup table/index into array

本文关键字:数组 数据类型 索引 查找      更新时间:2023-10-16

假设我有一个类'Widget'。在我的应用程序中,我创建了很多小部件(出于缓存局部性和其他原因),我将其保存在向量中。

为了高效查找,我想实现一个索引数据结构。为了解决这个问题,我们假设它是一个从 int 索引到 Widget 元素的简单查找表。我的问题是:查找表的内容应该是什么。换句话说,我应该用哪种类型替换问号

using LookupTable = std::vector<?>

我看到以下选项:

  • 引用(Widget&,或者更确切地说,因为它必须是可分配的:reference_wrapper
  • 指针(小部件*)
  • 小部件向量中的索引 (size_t)
  • 指向 Widget 向量的迭代器对象 (std::vector::iterator)

在这些选项中,索引似乎是唯一不会被矢量调整大小而失效的选项。我实际上可能能够避免调整大小,但是,像这样实现查找表意味着对矢量实现做出假设,从"解耦设计"的角度来看,这似乎是不合理的。

OTOH索引不是类型安全的:如果我从查找表中得到的东西是一个引用,我只能使用它来访问相应的小部件。使用size_t值,我可以做一些无意义的操作,例如将结果乘以 3。还要考虑以下两个签名:

void doSomethingWithLookupResult(Widget& lookupResult);
void doSomethingWithLookupResult(size_t lookupResult);

前者的描述性要强得多。

总结:我可以对查找表使用哪种数据类型来实现与向量实现的解耦和类型安全?

使用 std::vector::size_type

(不是 size_t)。 std::vector::size_type 在大多数实现中可能size_t,但为了可移植性和面向未来的考虑,我们会做对的。

继续制作一个类型定义:使用 WidgetIndex = std::vector::size_type;

这样看起来很合理:

void doSomethingWithLookupResult(WidgetIndex lookupResult);

这避免了矢量大小调整问题,当您在问题中淡化它时,最终会回来咬您。

不要使用某些用户定义的类型玩游戏,例如 tohava(非常聪明地)提议,除非您打算在代码库中大量使用此成语。为什么不这样做:

  • 你正在解决的问题(类型安全)是真实的,如果它是"免费的",我们希望有一个解决方案,但与程序员不得不搬起石头砸自己的脚的其他机会相比C++这不是一个大问题。
  • 你会浪费时间。你设计类的时间,然后是代码库的每个用户(包括你几个月后忘记实现之后的你自己)的时间,他们会盯着那个代码,不得不把它弄明白。
  • 在未来的某个时候,你会绊倒那个"有趣"的角落案例,我们现在都无法通过盯着这段代码看到它。

综上所述,如果你打算在你的代码库中经常使用这个习惯用法(你有许多存储在非常静态的向量或数组中的类),那么进行这项投资可能是有意义的。在这种情况下,维护负担分散在更多的代码上,并且对错误的容器使用错误的索引类型的可能性更大。

您可以创建一个表示索引的类,该索引还携带类型信息(在编译时)。

#include <vector>
template <class T>
struct typed_index {
    typed_index(int i) : i(i) {}
    template <class CONTAINER>
    T &operator[](CONTAINER &c) { return c[i]; }
    template <class CONTAINER>
    const T &operator[](const CONTAINER &c) { return c[i]; }
    int i;
};
int main() {
    std::vector<int> v1 = {0};
    std::vector<const char *> v2 = {"asd"};
    typed_index<int> i = 3;
    int z = i[v1];
    const char *s = i[v2]; // will fail
}