C++之萃取技术(traits)
为什么需要类型萃取(特化)
前面我们提到了迭代器,它是一个行为类似于smart pointer之类的东西,主要用于对STL容器中的对象进行访问,而且不暴露容器中的内部结构,而迭代器所指对象的型别称为该迭代器的value type;如果在实际的工程当中我们应该怎么获取STL容器中对象的value type 呢,这里面就需要用到C++中模板的特化了,我们先来看看下面的代码:
template <class T>
void Func()
{
cout << "非内置类型" << endl;
} template <>
void Func<int>()
{
cout << "内置的int类型" << endl;
}
我们可以通过特化来推导参数,如果我们自定义了多个类型,除非我们把这些自定义类型的特化版本写出来,否则我们只能判断他们是内置类型,并不能判断他们具体属于是个类型。
这时候,我们引入了内嵌型别,在类中将迭代器所指向的类型定义成value type,还定义些其他的型别,具体的见前面博客中所提到的迭代器的内嵌型别,迭代器:https://www.cnblogs.com/cthon/p/9206262.html
typedef bidirectional_iterator_tag iterator_category;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef __list_node<T>* link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
但是这种情况还是有缺口的,例如下面的代码:
template <class T>
struct MyIter
{
typedef T ValueType;
T* _ptr;
MyIter(T* p = 0)
:_ptr(p)
{} T& operator*()
{
return *_ptr;
}
}; template<class Iterator>
typename Iterator::ValueType
Func(Iterator it)
{
return *it;
}
这里虽然看上去没有什么问题,但并不是所有的迭代器都有内嵌型别的,例如原生指针,因为原生指针不是类型别,所以我们无法为原生指针定义内嵌型别。
我们还可以看看下面这个例子:
这里先给出代码:
//Vector的代码
template <class T, class Alloc = alloc>
class Vector
{
public:
//vector的内嵌型别
typedef T ValueType;
typedef ValueType* Pointer;
typedef const ValueType* ConstPointer;
typedef ValueType* Iterator;
typedef const ValueType* ConstIterator;
typedef const ValueType* ConstPointer;
typedef ValueType& Reference;
typedef const ValueType& ConstReference;
typedef size_t SizeType;
typedef size_t DifferenceType;
public:
Vector()
: _start(0)
, _finish(0)
, _EndOfStorage(0)
{} //Vector() Iterator Begin()
{
return _start;
} Iterator End()
{
return _finish;
} ConstIterator cBegin() const
{
return _start;
} ConstIterator cEnd() const
{
return _finish;
} ValueType& operator[](const SizeType& n)
{
return *(_start + n);
} //Const ValueType& operator[](const SizeType& n) const
//{
// return *(_start + n);
//} void PushBack(const ValueType& x)
{
//扩容
_CheckCapacity(); Insert(_finish, x);
} void Insert(Iterator pos,const ValueType& x)
{
//SizeType n = pos - _begin;
//assert(pos>=_start&&pos<=_finish);
if (pos == _finish)
{
Construct(_finish, x);
_finish++;
}
else
{
//计算插入点之后的现有元素的个数
size_t ElemFront = _finish - pos; /* _finish++;*/
for (int i = 0; i < ElemFront; i++)
{
Iterator CopyPos = _finish - i;
Construct(CopyPos, *(CopyPos - 1));
}
_finish++;
Construct(pos, x);
}
} protected:
typedef SimpleAlloc<ValueType, Alloc> DataAllocator;
Iterator _start;
Iterator _finish;
Iterator _EndOfStorage;
protected:
void _CheckCapacity()
{
if (_finish == _EndOfStorage)
{
//空间已满,开辟空间
size_t OldSize = _finish - _start;
size_t NewSize = OldSize * 2 + 3;
T* _tmp = DataAllocator::allocate(NewSize); //开辟新的空间
for (size_t i = 0; i < OldSize; i++)
{
//拷贝原数组当中的成员
_tmp[i] = _start[i];
}
//改变参数
swap(_start, _tmp);
_finish = _start + OldSize;
_EndOfStorage = _start + NewSize; //释放空间
DataAllocator::deallocate(_tmp, OldSize);
}
}
}; void TestVector()
{
Vector<int> v1; v1.PushBack(1);
v1.PushBack(2);
v1.PushBack(3);
v1.PushBack(4);
v1.PushBack(5);
v1.PushBack(6); Vector<int>::Iterator it =v1.Begin(); while (it != v1.End())
{
cout << *it << " ";
it++;
} cout << "Distance?" << Distance(v1.Begin(), v1.End()) << endl;
} //求两个迭代器之间距离的函数 //普通类型的求两个迭代器之间距离的函数
template <class InputIterator>
inline size_t __Distance(InputIterator first, InputIterator last, InputIteratorTag) {
size_t n = 0;
while (first != last) {
++first; ++n;
}
return n;
} //RandomAccessIteratorTag类型的求两个迭代器之间距离的函数
template <class RandomAccessIterator>
inline size_t __Distance(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIteratorTag)
{
size_t n = 0;
n += last - first;
return n;
} //求两个迭代器之间的距离的函数
template <class InputIterator>
inline size_t Distance(InputIterator first, InputIterator last)
{
return __Distance(first, last,InputIterator::IteratorCategory());
/*return __Distance(first, last,IteratorTraits<InputIterator>::IteratorCategory()); */
}
如果只有内嵌型别,会发生下面的情况:
这里图中已经给出了详细的解释。
原生指针本来应该是迭代器当中的RandomAccessIterator,这里因为无法为原生指针定义类型,所以我们就没有办法拿到原生指针的迭代器,这时候,类型萃取就出来了:
//类型萃取
template <class Iterator>
struct IteratorTraits
{
typedef typename Iterator::IteratorCategory IteratorCategory;
typedef typename Iterator::ValueType ValueType;
typedef typename Iterator::DifferenceType DifferenceType;
typedef typename Iterator::Pointer Pointer;
typedef typename Iterator::Reference Reference;
}; //原生指针的类型萃取
template <class T>
struct IteratorTraits<T*>
{
typedef RandomAccessIteratorTag IteratorCategory;
typedef T ValueType;
typedef ptrdiff_t DifferenceType;
typedef T* Pointer;
typedef T& Reference;
}; //针对const T*的类型萃取
template <class T>
struct IteratorTraits<const T*>
{
typedef RandomAccessIteratorTag IteratorCategory;
typedef T ValueType;
typedef ptrdiff_t DifferenceType;
typedef T* Pointer;
typedef T& Reference;
};
这时候,我们就能调用正确的__Distance函数了。
类型萃取的设计模式提高了代码的复用性,在上面的例子中通过萃取出不同的迭代器的类型来调用不同迭代器的__Distance函数,也提升了代码的性能。
参考:http://www.cnitblog.com/weitom1982/archive/2008/12/19/7889.html
C++之萃取技术(traits)的更多相关文章
- C++的类型萃取技术
应该说,迭代器就是一种智能指针,因此,它也就拥有了一般指针的所有特点——能够对其进行*和->操作.但是在遍历容器的时候,不可避免的要对遍历的容器内部有所了解,所以,设计一个迭代器也就自然而然的变 ...
- C++11中的技术剖析(萃取技术)
从C++98开始萃取在泛型编程中用的特别多,最经典的莫过于STL.STL中的拷贝首先通过萃取技术识别是否是已知并且支持memcpy类型,如果是则直接通过内存拷贝提高效率,否则就通过类的重载=运算符,相 ...
- C++萃取技术的一个简单初探
首先本文并不是讲解C++萃取技术,关于C++的萃取技术网上有很多文章,推荐http://www.cppblog.com/woaidongmao/archive/2008/11/09/66387.htm ...
- 类型萃取(type traits)
1. 类型萃取的作用 类型萃取使用模板技术来萃取类型(包含自定义类型和内置类型)的某些特性,用以判断该类型是否含有某些特性,从而在泛型算法中来对该类型进行特殊的处理用来提高效率或者其他.例如:在STL ...
- CGI 萃取技术 __type_traits
前言 上一篇文章讲了 iterator_traits 的编程技法,非常棒适度弥补了 C++ 语言的不足. STL 只对迭代器加以规范,制定了 iterator_traits 这样的东西. CGI 把这 ...
- STL源代码分析--萃取编程(traits)技术的实现
1.为什么要出现? 依照默认认定.一个模板给出了一个单一的定义,能够用于用户能够想到的不论什么模板參数!可是对于写模板的人而言,这样的方式并不灵活.特别是遇到模板參数为指针时,若想实现与类型的參量不一 ...
- STL--迭代器设计原则和萃取机制(Traits)
title: C++ STL迭代器设计原则和萃取机制(Traits) date: 2019-12-23 15:21:47 tags: STL C/C++ categories: STL 迭代器 (it ...
- c++11——type_traits 类型萃取
一. c++ traits traits是c++模板编程中使用的一种技术,主要功能: 把功能相同而参数不同的函数抽象出来,通过traits将不同的参数的相同属性提取出来,在函数中利用这些用tr ...
- STL 萃取(Traits)机制剖析
模板特化 在将萃取机制之前,先要说明模板特化 当有两个模板类,一个是通用泛型模板,一个是特殊类型模板,如果创建一个特殊类型的对象,会优先调用特殊的类型模板类,例如: template <type ...
随机推荐
- ffmpeg一些filter使用方法、以及一些功能命令
1.加字幕 命令:ffmpeg -i <input> -filter_complex subtitles=filename=<SubtitleName>-y <outpu ...
- 非常easy的JAVA反射教程
原创文章,转载请注明. 反射能够动态载入类,实例化对象,调用方法.如今以下面样例解说. 一.载入类. Class clazz = Class.forName("java.lang.Strin ...
- Java序列化算法
Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程.java序列化API提供一种处理对象序列化的标准机 ...
- jquery $.proxy使用 Jquery实现ready()的源码
jquery $.proxy使用 在某些情况下,我们调用Javascript函数时候,this指针并不一定是我们所期望的那个.例如: 1 //正常的this使用 2 $('#myElement') ...
- HDU 5667 :Sequence
Sequence Accepts: 59 Submissions: 650 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536 ...
- js如何获取手机的屏幕尺寸
var width = $(document.body).outerWidth();//手机的屏幕宽 var height = $(window).innerHeight(); //手机的屏幕高
- mac gem命令
$ gem sources -r https://rubygems.org/ (移除旧版本的镜像,如果你不知道你电脑上目前用的是什么镜像,可用 $ gem sources -l 来查看) $ g ...
- Java开发面试题
- Extjs-树 Ext.tree.TreePanel 动态加载数据
先上效果图 1.说明Ext.tree.Panel 控件是树形控件,大家知道树形结构在软件开发过程中的应用是很广泛的,树形控件的数据有本地数据.服务器端返回的数据两种.对于本地数据的加载,在extjs的 ...
- mongodb分页
1 什么是mongodb的分页 就是一次返回表中的连续若干行. 2 什么是sql分页 同样是返回表中的连续若干行. 3 如何实现sql分页 利用order by xxx limit xxx 4 如何实 ...