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

原文地址:https://www.cnblogs.com/cthon/p/9206281.html

时间: 2024-08-01 07:31:11

C++之萃取技术(traits)的相关文章

STL学习_萃取技术__type_traits

之前在学习STL库中的析构工具destory()时,提到过这样一句话,此函数设法找到元素的数值型别,进而利用__type_traits<>求取适当措施.一直难以理解,现在自己总结了下自己对萃取技术的理解. 让自己困惑的程序: template<class T> void destroy(T *pointer) { pointer->~T(); } template<calss ForwardIterator> void destroy(ForwardIterato

C++_模板类与类型萃取技术

在声明变量,函数,和大多数其他类型实体的时候,C++要求我们使用指定的类型.然而,有许多代码,除了类型不同之外,其余部分看起来都是相同的,比如,下面这个例子: bool IsEqual (int left, int right) {     return left == right; } bool IsEqual (const string& left , const string& right) {     return left == right; } void test() {   

迭代器与萃取技术

看了书和老师的讲解,这里大体说一下自己对迭代器和萃取技术的理解. 迭代器它是C++标准模板库里面的智能指针(smart pointer),由于STL设计时并不是以OOP思想为指导,而是以GP,所以让容器与算法分离实际,这么做的好处是可以让各个模块的设计者无需去关系其他模块的实现,从而专心于自己的模块,所以迭代器是用于连接容器和算法的桥梁. OOP(Object-Oriented programming)//面向对象的程序设计 GP(Generic Programming)//泛型程序设计 作为智

STL(七)之萃取技术

traits技术 原理:利用template的参数推导机制获取传入的参数型别. template<typename T> struct Iter { typedef T value_type; .... } template<typename T> typename T::value_type func(T* ite) {return *ite;} 这种程度,依旧会遇到一个问题:如果不是一个class type(比如指针,引用),就无法进行正确的参数推导.可以使用模板偏特化来处理这

STL源码分析--萃取编程(traits)技术的实现

1.为什么要出现? 按照默认认定,一个模板给出了一个单一的定义,可以用于用户可以想到的任何模板参数!但是对于写模板的人而言,这种方式并不灵活,特别是遇到模板参数为指针时,若想实现与类型的参量不一样的实例化,就变得不太可能了!也有时,想禁止此种相同的实例化变得不太可能!故而出现了,Partial Specialization! 同时,在使用void*指针时,可以最大限度的共享代码,减少代码的膨胀! 2.它是什么?其实,就是用户定义的偏特化.用template<>来说明这是一个偏特化,针对任何模板

STL源代码分析--萃取编程(traits)技术的实现

1.为什么要出现? 依照默认认定.一个模板给出了一个单一的定义,能够用于用户能够想到的不论什么模板參数!可是对于写模板的人而言,这样的方式并不灵活.特别是遇到模板參数为指针时,若想实现与类型的參量不一样的实例化.就变得不太可能了!也有时.想禁止此种同样的实例化变得不太可能! 故而出现了,Partial Specialization! 同一时候,在使用void*指针时.能够最大限度的共享代码,降低代码的膨胀! 2.它是什么?事实上,就是用户定义的偏特化.用template<>来说明这是一个偏特化

头一回发博客,来分享个有关C++类型萃取的编写技巧

废话不多说,上来贴代码最实在,哈哈! 以下代码量有点多,不过这都是在下一手一手敲出来的,小巧好用,把以下代码复制出来,放到相应的hpp文件即可,VS,GCC下均能编译通过 1 #include<iostream> 2 #include "../../traits/traits.hpp" 3 4 5 using namespace std; 6 7 8 int show(char i, int j) 9 { 10 return 1; 11 } 12 13 struct Stu

STL 萃取(Traits)机制剖析

模板特化 在将萃取机制之前,先要说明模板特化 当有两个模板类,一个是通用泛型模板,一个是特殊类型模板,如果创建一个特殊类型的对象,会优先调用特殊的类型模板类,例如: template <typename T> //泛型模板 class MyClass { public: MyClass() { cout << "T MyClass!" << endl; } ~MyClass() { cout << "~T MyClass!&qu

类型萃取

类型萃取是实现不同类型数据面对同一函数实现不同的操作,它与类封装的区别是:并不用知道所调用的对象是什么类型,类型萃取是编译后知道类型,先实现:而类的封装则是先定义类型,后实现方法. 类型分为基本类型(POD),和自定义类型. 在这里用模板的特化实现其编程思想: 以memcpy为例,当拷贝的是基本类型(POD)时,只用拷贝所传递指针上的数据,如果是string类型,则需要在堆上开辟空间,所传递的指针如果被直接复制,则有可能(vs下的string类型的实现原理是若字符串不长则以数组保存,若字符串过长