《STL源码剖析》学习记录:
模板编程中经常写到:
template<typename T>
void func0(T a)
{
T aa;
//do_sth();
return;
}
template<typename T>
T func1()
{
T tmp;
//do_sth();
return tmp;
}
当模板类型T为迭代器类型,而我们需要以迭代器所指类型为类型时,并不能如下所写:
template<typename T>
void func0(T a)
{
*T aa; //错误
//do_sth();
return;
}
template<typename T>
(*T) func1(T a) //错误
{
//do_sth();
return *a;
}
前者可以利用函数模板的参数推导来实现
template<typename T>
void func0(T a)
{
//*T aa;
func0_imp(*a);
//do_sth();
return;
}
template<typename T>
void func0_imp(T a)
{
T aa;
//do_sth();
return;
}
后者模板参数推导机制却不能解决问题,并不能推导出函数的返回值类型。这时便需要内嵌类型来解决了。
template<typename T>
class Iterator
{
typedef T value_type;//内嵌类型声明
//省略类成员...
};
template<typename I>
typename I::value_type //返回值类型,必须加typename告诉编译器这是类型
func(I iter)
{
return *iter;
}
对于指针类型或const需要对模板进行偏特化。
参考https://blog.csdn.net/uestc_chenmo/article/details/80412353
由此便引出了traits。traits便能够萃取出迭代器中的特性,如上述value_type等。通过增加一层traits间接性,不仅能够拥有特化版本,也能够对其特性进行更好的集成封装。
通常,迭代器常见有五种特性: value_type, difference_type, reference_type, pointer_type,iterator_category。因此这些都在 traits和其相应偏特化中提取出来。
其中,iterator_category标识根据迭代器的移动特性和对迭代器执行的操作上的区别和特性,将迭代器分为Input Iterator(只读)、Output Iterator(只写)、Forward Iterator(前向)、Bidirectional Iterator(双向)、Random Access Iterator(随机访问)五类。
struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
// 然后4 5 依次单继承。STL命名规定,传入最初级类型命名参数,
//函数调用时假设传进一个更高级的参数,因为继承的原因,会沿继承向上传递调用
STL中有iterator类,该类不含任何成员,只是类型的定义,继承此类并不会有额外开销,迭代器通过继承该类可以规范地使用traits。
做笔记的时候突然想到的auto和decltype,略微试验了下,链接如下:
https://blog.csdn.net/uestc_chenmo/article/details/80444581