注意:当然,我认为您实现哈希表的目标是出于学习目的。如果没有,请使用std::unordered_map。
这里最重要的一点是您已经注意到的:模板和泛型不是一回事。 (好吧,java 泛型只是一个花哨的语法糖工具,这要归功于类型擦除,但这是另一回事)。
C++ 模板可以为模板的每个不同实例编写一个类的版本。也就是说,如果在您的程序中使用std::vector<int> 和std::vector<bool>,编译器会生成类型为int 的类向量和类型为bool 的类向量的代码。
模板最强大的一个方面是每个与类型相关的操作都在编译时进行评估。也就是说,每个模板实例化、别名、typedef 等都是在编译时完成的。您在运行时获得的代码是为不同模板实例最终生成的类的组合。因此,您不必考虑运行时的类型,例如 java 或 C# 等 OO 语言,而是考虑编译时。 编译结束时必须解决所有问题。
现在我们来看看您的问题:
您需要为您的类型提供一个“可散列的”“接口”,以调用散列表中的散列函数。
这可以通过两种方式完成:
基于成员函数的方式:
解决你的问题的一种方法是假设你的类型有一个hash()公共成员函数(就像java中的getHashCode(),它继承自Object)。
所以,在你的哈希表实现中,你使用这个哈希函数,就好像元素有它一样。不用担心传递的类型。
事实是,如果您这样做,并且您将类型作为模板参数传递,但该类型没有公共散列成员函数,则无法实例化模板。中提琴!
将模板视为合约:您在模板中编写完全通用的代码。传递给模板的类型必须满足合同。也就是说,必须拥有你认为他们拥有的一切。
这种方法的问题是在 hashmap 中使用的任何类型都必须有一个 hash public 成员函数。请注意,您不能通过这种方式在哈希图中使用基本类型。
基于函子的方式:
functor 是一个充当函数的类。也就是说,该类的实例可以像函数一样使用。例如:
template<typename T>
struct add
{
T operator()(const T& a , const T& b)
{
return a + b;
}
};
您可以按如下方式使用此函子:
int main()
{
add<int> adder;
int a = 1 , b = 2;
int c = adder(a,b);
}
但函子最重要的用途是基于 函子是类的实例,因此可以作为参数传递给其他网站。也就是说,仿函数充当高级函数指针。
这用于通用 STL 算法,如 std::find_if:Find if 用于根据搜索条件查找指定区间的元素。该标准通过充当布尔谓词的函子传递。例如:
class my_search_criteria
{
bool operator()(int element)
{
return element == 0;
}
};
int main()
{
std::vector<int> integers = { 5 , 4 , 3 , 2 , 1 , 0 };
int search_result = std::find_if( std::begin( integers ) , std::end( integers ) , my_search_criteria() );
}
但是,函子如何帮助解决您的问题?
您可以实现一个充当散列函数的通用仿函数:
template<typename T>
struct hash
{
unsigned int operator()(const T& element)
{
return /* hash implementation */
}
};
并在您的哈希表类中使用它:
template<typename T>
class hachtable
{
private:
hash<T> hash_function;
std::vector<T> _container;
void add(const T& element)
{
_container.insert(std::begin( _container ) + hash_function( element ) , element);
}
};
请注意,您需要为元素的类型实现哈希。
C++ 模板允许您编写模板的特殊显式案例。例如,您编写了一个通用数组类,并且您注意到如果元素的类型是布尔值,那么将布尔值存储为数字位会更有效,以减少内存消耗。使用 C++ 模板,您可以编写特殊情况。您使用显式类型作为模板参数显式编写模板类。这被称为"template specialization"。事实上,那个“使用位用于布尔大小写”的例子正是std::vector does。
在我们的例子中,如果我们有一个哈希函子的声明:
template<typename T>
struct hash;
我们需要对您将在哈希图中使用的每种类型进行专门化。例如,无符号整数的特化:
template<>
struct hash<unsigned int>
{
unsigned int operator()(unsigned int element)
{
return element;
}
};
基于函子的方式正是 C++ 标准库所做的。它有一个哈希函子的定义,std::hash,并在哈希表实现中使用它,比如std::unordered_map。请注意,该库具有一组用于基本类型的内置哈希特化。