我正在使用我正在使用的代码的另一部分中定义的类型Id
:
typedef int Id;
现在我提供了许多对象,每个对象都带有这样的Id
,我想使用Id
作为存储这些对象的std::vector
的索引。它可能看起来像这样:
std::vector<SomeObj*> vec(size);
std::pair<Id, SomeObj*> p = GetNext();
vec[p.first] = p.second;
问题是std::vector
使用自己的类型来索引其元素:std::vector::size_type
(为什么不是模板化的?)。
严格来说,使用std::map<Id, SomObj*>
会更好,但效率会降低,而且数组就是我真正需要的(我知道所有对象的索引都是连续的并以{开头} {1}})。另一个问题是0
将来可能会更改为typedef int Id
或类似...(虽然它是我自己代码的一部分,所以我控制它,但理想情况下我应该允许更改typedef long int Id
在某些方面;这就是typedef的用途。)
你会怎么处理这个?也许使用typedef
,其中哈希函数直接使用unordered_map<Id, SomeObj*>
作为哈希键?这会降低内存效率吗? (我不完全理解Id
如何预先知道散列函数的范围是否会分配其空间?)
答案 0 :(得分:4)
您可以将任何整数类型作为std::vector
的索引传递。如果它与std::vector<T>::size_type
(通常为unsigned long
)不匹配,则会隐式转换该值。
答案 1 :(得分:3)
为什么不是那个模板?
因为标准容器被实现为使用它们合理可以使用的最大无符号类型。如果您的实施中size_type
为unsigned int
,那么这是有原因的,无论原因是什么阻止了使用更大类型的实施者,如果您要求其他东西[*],它仍然存在。此外,对于您的特定示例,大小类型必须是无符号的,并且您希望使用签名类型,因此这是支持您想要执行的操作所需的另一个更改。
在实践中,标准容器的size_type
(几乎?)总是size_t
。因此,如果您要求更大的向量,则不能有一个向量,因为向量由连续存储支持。向量将无法分配大于size_t
字节的数组。
要使用Id
作为向量索引,您可以依赖隐式转换,也可以显式转换(也可以显式绑定检查),以便清楚地了解您正在做什么。您还可以使用断言来确保Id
不大于size_type
。像这样的东西,虽然静态断言可能会更好:
assert(std::numeric_limits<Id>::max() <= std::numeric_limits<std::vector<SomeObj*>::size_type>::max());
如果使用的Id值稀疏,则map<Id, SomeObj*>
将是一个不错的选择。如果唯一有效的Ids是1和400,000,000,则向量将相当浪费内存。
如果它让您感觉更舒服,请记住文字0
的类型为int
,而不是vector<SomeObj*>::size_type
。大多数人对编写vec[0]
没有任何疑虑:确实在标准中使用了它。
[*]即使这个原因只是,“实施者认为40亿元素足以让任何人”。
答案 2 :(得分:2)
编写自己的容器包装器,将Id
作为索引类型。在内部使用map
或unordered_map
来实现容器。针对此包装器的程序。如果事实证明此实施太慢,请在内部切换到vector
并将Id
索引转换为vector::size_type
(当然也在内部)。
这是最干净的方法。但实际上,vector::size_type
将是一些非常大的无符号整数类型,因此从Id
到vector::size_type
的转换始终是安全的(但不是相反!)。
答案 3 :(得分:0)
问题是,Id
是int
(更确切地说,signed int
),可能是否定的。如果Id
是无符号类型,例如typedef unsigned int Id;
,没有问题。
如果到目前为止我的理解是正确的,那么我不明白为什么有人想要使用负数作为vector
(或array
)的索引。我错过了什么?