C++——Traits编程技法

 

——这篇是直接根据侯捷老师的书写的,几乎没有自己加工的部分,不过也是学习的总结吧

Traits编程技法

按照顺序,这次应该是迭代器Iterator的内容了,然而Iterator涉及到一个重要的技巧就是Traits编程技法;它还是值得单独一章来介绍一下的。

一 获取Iterator的相应类型(associate type

在使用Iterator时,可能需要知道它的相应类型,也就是Iterator指向的变量的类型,在C/C++语言中,如果要获取一个变量的大小可以使用sizeof()操作符。然而如果想要获取一个指针指向的变量类型该如何做呢,可惜它没有一个typeof()操作符供我们程序员使用。

 

利用template的引数/参数推导(argument deducation)是一个解决问题的好方法,仅将func函数作为一个包装,而把实际的操作放在一个函数func_impl里面完成。一旦func()函数被调用,编译器就自动进行引数推导,自动导出类型T

 

template <class I>

inline void func(I iter)

{

    func_impl(iter, *iter); // 一层封装

}

 

template <class I, class T>

void func_impl(I iter, T t)

{

    T tmp; // 在本例中,t就是int类型

    tmp = t;

    cout<<tmp<<endl; // tmpint类型,可以直接输出

}

 

int main()

{

    int i = 4;

    func(&i);

    return 0;

}

 

看上去不错,虽然多了一层包装,但是还是可以工作的很好。好了,现在想想另一种情况,如果要将这个类型作为一个函数,比如上面的func的返回类型,该怎么办呢。毕竟引数推导导出的只是引数,没有办法应用于函数的返回值。看来我们需要另外的方法来解决这一问题,这就引出了本章的一个重要技巧Traits编程技法。

 

 Traits编程技法初见

采用nested type(巢状型别)似乎是个不错的注意,如下所示:

 

template <class T>

class Iterator

{

public:

    typedef T value_type;

    T *m_ptr;

    Iterator(T *p = 0) : m_ptr(p) {}

    T& operator *() const {return *m_ptr;}

    // ...

};

 

template <class I>

typename I::value_type func2(I iter)

{

    return *iter;

}

 

int main()

{

    int *p = new int(8);

    Iterator<int> iter(p);

    cout<<func2(iter)<<endl;

    delete p;

    return 0;

}

这里func2函数的返回值前加上了一个typename,这是因为在template T实例化之前,编译器对T一无所知,并不知道Iterator<int>::value_type代表的是一个函数,变量还是类型。关键字typename就是告诉编译器说这是一个类型,以使得编译通过。

看起来不错,但是这里还有一个隐晦的陷阱:并不是所有的迭代器都有value_type,编译器内嵌类型(原生指标)就没有,这样编译就不能通过,但是STL必须接受原生指标作为一种迭代器,这需要另外的技巧,它就是模板偏特化(template partial specialization

 

转载自:http://blog.csdn.net/sparkliang/archive/2009/03/20/4008096.aspx

 

补充:

 什么是C++ Traits? 并举例说明

首先假如有以下一个泛型的迭代器类,其中类型参数 T 为迭代器所指向的类型:

template
  <typename   T>
class   myIterator
{
 ...
};

当我们使用myIterator时,怎样才能获知它所指向的元素的类型呢?我们可以为这个类加入一个内嵌类型,像这样:
template   <typename   T>
class   myIterator
{
      
typedef  T value_type; 
...
};
这样当我们使用myIterator类型时,可以通过 myIterator::value_type来获得相应的myIterator所指向的类型。

现在我们来设计一个算法,使用这个信息。
template   <typename T>
typename
  myIterator<T>::value_type Foo(myIterator<T> i)
{
 ...
}
这里我们定义了一个函数Foo,它的返回为为  参数i 所指向的类型,也就是T,那么我们为什么还要兴师动众的使用那个value_type呢? 那是因为,当我们希望修改Foo函数,使它能够适应所有类型的迭代器时,我们可以这样写:
template   <typename I>   //这里的I可以是任意类型的迭代器
typename   I::value_type Foo(I i)
{
 ...
}
现在,任意定义了 value_type内嵌类型的迭代器都可以做为Foo的参数了,并且Foo的返回值的类型将与相应迭代器所指的元素的类型一致。至此一切问题似乎都已解决,我们并没有使用任何特殊的技术。然而当考虑到以下情况时,新的问题便显现出来了:

原生指针也完全可以做为迭代器来使用,然而我们显然没有办法为原生指针添加一个value_type的内嵌类型,如此一来我们的Foo()函数就不能适用原生指针了,这不能不说是一大缺憾。那么有什么办法可以解决这个问题呢? 此时便是我们的主角:类型信息榨取机 Traits 登场的时候了

我们可以不直接使用myIteratorvalue_type,而是通过另一个类来把这个信息提取出来:
template   <typename   T>
class Traits
{
      
typedef typename T::value_type value_type;
};

这样,我们可以通过 Traits<myIterator>::value_type 来获得myIteratorvalue_type,于是我们把Foo函数改写成:
template   <typename I>   //这里的I可以是任意类型的迭代器
typename   Traits<I>::value_type Foo(I i)
{
 ...
}
然而,即使这样,那个原生指针的问题仍然没有解决,因为Trait类一样没办法获得原生指针的相关信息。于是我们祭出C++的又一件利器--偏特化(partial specialization)
template   <typename   T>
class Traits<T*> 
//注意 这里针对原生指针进行了偏特化
{
      
typedef   typename   T value_type;
};
通过上面这个 Traits的偏特化版本,我们陈述了这样一个事实:一个 T* 类型的指针所指向的元素的类型为 T

如此一来,我们的 Foo函数就完全可以适用于原生指针了。比如:
int   * p;
....
int   i = Foo(p);
Traits
会自动推导出 p 所指元素的类型为 int,从而Foo正确返回。

 

你可能感兴趣的:(编程,C++,算法,iterator,编译器,nested)