文章来自微信公众号“科文路”,欢迎关注、互动。转发须注明出处。
traits 在 MLIR 体系中被大量使用。它的本意很晦涩,“萃取信息”,有点像是隐含的重要信息。
简单讲,C++ 中的 traits 概念指代一种编程方法,它抽象一种接口,使得这个接口针对不同类型的输入作分别处理。
技术本质上,traits 总被实现为一个类,插入接口和实现之间。
更多的,像 STL 中,就是用这些方法来实现了算法的类型无关的抽象。
下面一步步走近 traits。
1. template,推导参数类型
先看代码,
1 2 3 4 5 6 7 8 9 10 11 12 13
| template <class I, class T> void func_impl(I iter, T t) { T tmp; }
template <class I> inline void func(I iter) { func_impl(iter, *iter); }
int main() { int i; func(&i); }
|
这是一段迭代器相关的代码。
在上述代码中,func(&i)
通过中间层 func_imp
自动推导出了 &i
所指向的数据类型,也就是 T
。
2. 偏特化,调整匹配顺序
偏特化其实就是在 (全)特化 啥都能传进去的模板类上,加了一些限制。例如下面的 <T *>
就使得这个类比上面的那个更 “特别”,这么做就能在遇到相应参数时得到优先的匹配权。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <iostream>
template <class T> struct iterator_traits { typedef typename T::value_type value_type; };
template <class T> struct iterator_traits<T *> { typedef T value_type; };
template <class I> typename iterator_traits<I>::value_type func(I ite) { return *ite; }
int main() { int a = 1; std::cout << func(&a) << std::endl; return 0; }
|
从下往上看。
在 func
使用 I
时,I
将去匹配一个 iterator_traits
。在这里,它将直接匹配到中间那个 <T *>
,也就是偏特化的版本。
那么,当使用 func
函数的时候,传入的参数就得是 T *
,否则报错。
所以最终得到的 value_type
是 T
而不是 I::value_type
,这也就实现了文章开头所说的同样的接口,不同的处理。
3. 回过头来,一个例子
下面这个例子体现了 traits 的作用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <iostream> #include <vector>
template <class T> struct iterator_traits { typedef typename T::value_type value_type; };
template <class T> struct iterator_traits<T *> { typedef T value_type; };
void func(int a) { std::cout << "func(int) is called" << std::endl; }
void func(double a) { std::cout << "func(double) is called" << std::endl; }
void func(char a) { std::cout << "func(char) is called" << std::endl; }
int main() { iterator_traits<std::vector<int>::iterator>::value_type a; func(a);
iterator_traits<std::vector<double>::iterator>::value_type b; func(b);
iterator_traits<char *>::value_type c; func(c); return 0; }
|
好了,就是这样,虽然看似复杂,其实实现的逻辑也没有那么难,对吧?
参考
都看到这儿了,不如关注每日推送的“科文路”、互动起来~
至少点个赞再走吧~