C++的traits技术

 

对于自定义的结构体,如果需要作为要求元素是可哈希的容器的元素,那么就需要自定义hash函数。STL中这样的容器有:std::unordered_mapstd::unordered_setstd::unordered_multimapstd::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,此时没有被省略

参考资料

  1. mangoyuan: c++ traits 技术浅谈
  2. haozlee: 细说 C++ Traits Classes
  3. 于小咸 c++的traits技术到底是什么
  4. 《Effective C++ 改善程序与设计的55个具体做法》 条款47