先写点题外话。本来是想在本科阶段写点儿所谓的技术博客的,可惜最后没有实现。究其原因,不外乎懒散而已。现在也是时候付诸行动了,写点儿东西。随笔之类的东西我写了不少,博客我想还是尽量以学习技术为主吧,偶尔穿插点我个人的感悟和废话,呵呵。好了,闲聊到此结束,开始正题。
读了研之后决定学学C++,因此这个系列很俗套地叫作“C++学习笔记”,今天的主要讨论对象是模版。 我对C++并没有完整的学习和研究,primer没有通读过,只是作为参考手册在遇到问题或感兴趣的章节时查阅一下,因此我的表述会有不严谨的地方,欢迎批评指正。
模版大家应该都不陌生
template<typename T>
class A{};
以上就是一个最简单的模版类。顺便说点细节问题:第一个细节是typename也可以换成class,并且更常用,但从含义上来说,typename更准确,因为模版参数可以为基本类型,而其不是class。typename还有另一个作用,即告诉编译器某个标识符是一个type,这在泛型和模版编程中很有用,因为在某些情况下,模版参数进行类型的动态绑定之前,编译器不能在静态编译时期识别某个标识符是否为一个类,比如T::value_type,那么,这是一个type,还是一个member function亦或者data member呢?T是一个type,但除此之外,我们对其一无所知,它的内部构造是透明的,编译器自然无法辨别,因此需要在之前加上typename关键字指明T::value_type为一个type,这样才能通过编译。
第二个细节:将class换成struct也可以。大家可能认为struct只是一些数据的集合体,其实在C++里,struct可以很复杂,复杂到什么程度?在功能上与class没有区别。当然,这是目前我所掌握的信息,究竟有没有本质区别,我可不敢打保票。一个class和struct可能惟一的区别是默认访问和继承权限,class的成员默认权限是private,struct是public。我当然不是瞎说的,也查了些资料。在Lippman的《Inside the C++ Object Model(中译本)》中和stroustrup(C++之父)的《The C++ Programming Language(中译本)》中应该是这么说的。stroustrup在P208 10.2.8节中明确写道:一个struct也就是一个类,但其成员默认为公有的。事实上,在STL源码中,许多模版类是用struct声明的
template<typename _Category, typename _Tp, typename _Distance = ptrdiff_t,
typename _Pointer = _Tp*, typename _Reference = _Tp&>
struct iterator
{
/// One of the @link iterator_tags tag.
typedef _Category iterator_category;
/// The type "pointed to" by the iterator.
typedef _Tp value_type;
/// Distance between iterators is represented as this type.
typedef _Distance difference_type;
/// This type represents a pointer-to-value_type.
typedef _Pointer pointer;
/// This type represents a reference-to-value_type.
typedef _Reference reference;
};
#if __cplusplus >= 201103L
_GLIBCXX_HAS_NESTED_TYPE(iterator_category)
template<typename _Iterator,
bool = __has_iterator_category<_Iterator>::value>
struct __iterator_traits { };
template<typename _Iterator>
struct __iterator_traits<_Iterator, true>
{
typedef typename _Iterator::iterator_category iterator_category;
typedef typename _Iterator::value_type value_type;
typedef typename _Iterator::difference_type difference_type;
typedef typename _Iterator::pointer pointer;
typedef typename _Iterator::reference reference;
};
template<typename _Iterator>
struct iterator_traits
: public __iterator_traits<_Iterator> { };
#else
template<typename _Iterator>
struct iterator_traits
{
typedef typename _Iterator::iterator_category iterator_category;
typedef typename _Iterator::value_type value_type;
typedef typename _Iterator::difference_type difference_type;
typedef typename _Iterator::pointer pointer;
typedef typename _Iterator::reference reference;
};
#endif
/// Partial specialization for pointer types.
template<typename _Tp>
struct iterator_traits<_Tp*>
{
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef _Tp& reference;
};
/// Partial specialization for const pointer types.
template<typename _Tp>
struct iterator_traits<const _Tp*>
{
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef const _Tp* pointer;
typedef const _Tp& reference;
};
/**
* This function is not a part of the C++ standard but is syntactic
* sugar for internal library use only.
*/
template<typename _Iter>
inline typename iterator_traits<_Iter>::iterator_category
__iterator_category(const _Iter&)
{ return typename iterator_traits<_Iter>::iterator_category(); }
//@}
// If _Iterator has a base returns it otherwise _Iterator is returned
// untouched
template<typename _Iterator, bool _HasBase>
struct _Iter_base
{
typedef _Iterator iterator_type;
static iterator_type _S_base(_Iterator __it)
{ return __it; }
};
template<typename _Iterator>
struct _Iter_base<_Iterator, true>
{
typedef typename _Iterator::iterator_type iterator_type;
static iterator_type _S_base(_Iterator __it)
{ return __it.base(); }
};
#if __cplusplus >= 201103L
template<typename _InIter>
using _RequireInputIter = typename
enable_if<is_convertible<typename
iterator_traits<_InIter>::iterator_category,
input_iterator_tag>::value>::type;
#endif
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
#endif /* _STL_ITERATOR_BASE_TYPES_H */
如上,是iterator与iterator_traits的定义,在bits/stl_iterator_base_types.h文件中,贴出来只是让大家参考,并不是说我掌握了哈哈。
我的话是不是有点多啊,说到现在都没说到正题。。我也不会排版,估计很难看,以后再说吧。。。
大家应该看到了,在iterator定义中,总是出现value_type一类的标识符,为什么呢?因为C++是静态类型语言,在声明和定义变量时需要明确指定变量的type,而为了实现泛型编程(Generic Programming),出现了模版(template)。
template<typename T>
这样的声明可以使代码通过编译,但如何知道T究竟是什么类型呢?C++提供了模版推导机制,这里就不说了,篇幅有限。。。我所说的东西可以在侯捷的《STL源码剖析》里找到。语言提供的模版推导机制有局限性,需要进一步实现。一个iterator如何知道自己所指向对象(包括内置基本类型)的类型和性质呢,可以在其中用value_type等字段指明。而为了支持原生指针类型,又需要再加一层封装,就是iterator_traits。具体仍然可以参见上书,不是三两句话就能完事的,等我再学习学习,也许会另写一篇文章。
好了,开始下一个讨论:C++类模版的特化。
特化包括全特化( Full Specialization)和偏特化(Partial Specialization)。这两个名词的定义似乎不是那么严格,先不管吧。
#include <iostream>
using namespace std;
template<typename T, typename U>
class A{
public:
const static int a = 0;
};
template<typename U>
class A<int, U>{
public:
const static int a = 1;
};
template<>
class A<int, float>{
public:
const static int a = 2;
};
int main()
{
cout << A<int, float>::a << endl;
return 0;
}
以上代码分别定义了一个模板类、一个偏特化版本、一个全特化版本。近似来说,全特化就是全部特化,即针对所有的模板参数进行特化,偏特化就是部分特化,即针对部分模板参数进行特化。代码打印的结果是2,所以调用的是全特化版本的类,还是近似地说,编译器会调用特化程度最高的类(这句话只是根据实验推断的,我并没有查阅相关理论依据)。这应该也是个比较容易理解、很自然的特性,就好像局部变量覆盖同名全局变量一样。至于函数模版,只能全特化,不能偏特化,我似乎记得在某本书上说不是因为无法实现,只是没有必要而已,偏特化的功能可以通过函数重载实现,网上也是这么说的~
先暂时写到这,等有时间再修改或者再写一篇。希望能坚持下去,欢迎一起交流学习。