对于自定义的结构体,如果需要作为要求元素是可哈希的容器的元素,那么就需要自定义hash函数。STL中这样的容器有:std::unordered_map
、std::unordered_set
、std::unordered_multimap
、std::unordered_multiset
。
Traits 技术
图源《STL源码剖析》
可以简单的认为是鹦鹉学舌技术,即只是简单的 echo 传进来的对象的各种相关类型。
目前笔者的理解:抽象出几种类型之间的共性,将其作为接口,然后通过 traits 判别其特定的类别。从而实现同一个接口处理不同的类别。
必要性
STL中算法和容器之间是互相独立的,他们之间是通过迭代器连接起来的。
那么当 template 接口暴露给外界的是一个迭代器,那么需要如何判别它是 input/output/forward/bidirection/random_access iterator?这时候就可以通过 traits 技术,echo 一下这个迭代器到底是何出身。
实现2.4
在 C++ 中,traits 习惯上总是被实现为 struct
,但它们往往被称为 traits classes。Traits classes 的作用主要是用来为使用者提供类型信息<sup4</sup>。
针对迭代器的实现
template <typename IterT>
struct iterator_traits {
typedef typename IterT::value_type value_type; // 这个类型定义要求输入到这个借口的迭代器都有从属类型value_type,即 IterT::value_type
};
用户可以在自定义类型中自己定义 value_type 等等必要的类型信息,但是 内置的指针 是没有 value_type 等类型定义的,因此我们需要针对 原始指针 做一个偏特化。
针对原生指针做偏特化
template <typename IterT> // 注意此处为 <IterT> 而不是 <>,说明并不是全特化
struct iterator_traits<IterT*> {
typedef IterT value_type; // 这个类型定义要求输入到这个借口的迭代器都有从属类型value_type,即 IterT::value_type
};
测试2
// this is my_iter_traits.h
#idndef TEST_MY_ITER_TRAITS_H
#define TEST_MY_ITER_TRAITS_H
// 将上面的 template iterator_traits 和其偏特化放到此处
#endif //TEST_MY_ITER_TRAITS_H
// this is main.cpp
#include "my_iter_traits.h"
#include <vector>
void fun(int a) {
cout << "fun(int) is called" << endl;
}
void fun(char a) {
cout << "fun(char) is called" << endl;
}
int main() {
iterator_traits<vector<int>::iterator>::value_type a;
func(a); // 调用 void fun(int a)
iterator_traits<char*> b;
func(b); // 调用 void fun(char a)
return 0;
}
// 注意 char* 的使用:针对偏特化的这条语句
// template <typename IterT> struct iterator_traits<IterT*>
// 我们使用 iterator_traits<char*>,说明 char=Iter,*还是要自己补充出来。
// 其实模板不省略的话,应该写为:
// template <typename IterT> struct iterator_traits<IterT> {...};
// 注意看 iterator_traits<IterT> 中的 IterT,此时没有被省略
参考资料
- mangoyuan: c++ traits 技术浅谈
- haozlee: 细说 C++ Traits Classes
- 于小咸 c++的traits技术到底是什么
- 《Effective C++ 改善程序与设计的55个具体做法》 条款47