剖析C++模板(中)

转载 2006年05月30日 22:41:00
函数模板中的类型归纳 

  一个非常简单但很有用的例子:

  //: :arraySize.h 

  // Uses template type induction to 

  // discover the size of an array 

  #ifndef ARRAYSIZE_H 

  #define ARRAYSIZE_H 

  template<typename T, int size> 

  int asz(T (&)[size]) { return size; } 

  #endif // ARRAYSIZE_H ///:~ 

  没有使用sizeof()操作,却能在编译阶段指出数组的大小,你可以有更多简明的方法来计算编译时数组的大小。 

  //: C03:ArraySize.cpp 

  // The return value of the template function 

  // asz() is a compile-time constant 

  #include "../arraySize.h" 

  int main() 

  { 

  int a[12], b[20]; 

  const int sz1 = asz(a); 

  const int sz2 = asz(b); 

  int c[sz1], d[sz2]; 

  } ///:~ 

  当然,程序正常运行的前提是:数组在定义时就已经给出了大小。 

  取得一个已经实例化的函数模板的地址 

  有很多地方,你都需要取得函数入口地址,例如,你可能有一个函数,它的参数是一个指向另一个函数的指针,当然了,

这个被指的函数有可能是从一个模板中生成的,所以,你需要一种方法来取得这样的函数地址。

  //: C03:TemplateFunctionAddress.cpp 

  // Taking the address of a function generated 

  // from a template. 

  template <typename T> void f(T*) {} 

  void h(void (*pf)(int*)) {} 

  template <class T> 

  void g(void (*pf)(T*)) {} 

  int main() 

  { 

  // Full type exposition: 

  h(&f<int>); 

  // Type induction: 

  h(&f); 

  // Full type exposition: 

  g<int>(&f<int>); 

  // Type inductions: 

  g(&f<int>); 

  g<int>(&f); 

  } ///:~ 

  这个例子讲述了许多的不同的主题。首先,即使你在使用模板,类型必须匹配——函数h()取了一个指向函数指针,这个函

授接受int类型的参数返回void类型。而这正是f的特点。第二,需要函数指针作为参数的这个函数本身也可以是一个模板,就

像g一样。在main()函数中,你可以看到类型归纳同样也在这里工作。第一次调用h()明确的给出了模板参数f,但是从h()中看

到它仅仅处理使用int型变量做参数的函数的函数指针,那一部分可以由编译器来归纳。G()的使用还要有意思些,因为这里有两

个模板要使用。编译器不能归纳出来这个类型,也就什么都不会做,但如果f和g都赋成int,其它的对编译器来说也就好办了。 

  模板中的成员类 

  向STL系列中应用函数

  假设你想使用一个STL系列的容器,并且向容器中包含的所有对象应用一个函数,我之所以这样说是因为一个vector可以包

含各种类型的对象,你需要一个函数可以同vector协同工作,处理它所包含的各种对象:

  //: C03:applySequence.h 

  // Apply a function to an STL sequence container 

  // 0 arguments, any type of return value

  template<class Seq, class T, class R> 

  void apply(Seq& sq, R (T::*f)()) 

  { 

  typename Seq::iterator it = sq.begin(); 

  while(it != sq.end()) 

  { 

  ((*it)->*f)(); 

  it++; 

  } 

  } 

  // 一个参数,返回值不定 

  template<class Seq, class T, class R, class A> 

  void apply(Seq& sq, R(T::*f)(A), A a) 

  { 

  typename Seq::iterator it = sq.begin(); 

  while(it != sq.end()) 

  { 

  ((*it)->*f)(a); 

  it++; 

  } 

  } 

  //两个参数,返回值不定 

  template<class Seq, class T, class R, 

  class A1, class A2> 

  void apply(Seq& sq, R(T::*f)(A1, A2), 

  A1 a1, A2 a2) 

  { 

  typename Seq::iterator it = sq.begin(); 

  while(it != sq.end()) 

  { 

  ((*it)->*f)(a1, a2); 

  it++; 

  } 

  } 

  // 诸如此类, 传递最多的类似的参数 ///:~ 

  apply()函数模板使用了对容器类的一个引用和一个容器类内部成员函数的成员指针。它使用一个iterator在Stack中移动

定位向各个对象套用这个函数。 

  注意这里没有任何STL的头文件包含在applySequence.h中,所以它在与STL系列共同使用时,没有限制。当然,在使用的时

候,考虑到iterator的特殊性,我们也只能把它应用到STL系列中了(别忘了,我们必须使用iterator)。 

  你可以看到,这里有不止一个的apply(),因此它可以重载函数模板,每一个版本都使用了不同数量的参数,因为它是一个

模板,这些参数可以为任何类型,唯一的限制是:这不是超级模板可以为你创建出新的模板。 

  测试一下我们不同版本的apply()。 

  //: C03:Gromit.h 

  // The techno-dog. Has member functions 

  // with various numbers of arguments. 

  #include <iostream> 

  class Gromit 

  { 

  int arf; 

  public: 

  Gromit(int arf = 1) : arf(arf + 1) {} 

  void speak(int) 

  { 

  for(int i = 0; i < arf; i++) 

  std::cout << "arf! "

  std::cout << std::endl; 

  } 

  char eat(float) 

  { 

  std::cout << "chomp!" << std::endl; 

  return 'z'

  } 

  int sleep(char, double) 

  { 

  std::cout << "zzz..." << std::endl; 

  return 0; 

  } 

  void sit(void) {} 

  }; ///:~ 

  现在,函数apply()可以同vector<Gromit*>协同使用来制作一个容器,调用成员函数和被包含的对象: 

  //: C03:applyGromit.cpp 

  // Test applySequence.h 

  #include "Gromit.h" 

  #include "applySequence.h" 

  #include <vector> 

  #include <iostream> 

  using namespace std; 

  int main() 

  { 

  vector<Gromit*> dogs; 

  for(int i = 0; i < 5; i++) 

  dogs.push_back(new Gromit(i)); 

  apply(dogs, &Gromit::speak, 1); 

  apply(dogs, &Gromit::eat, 2.0f); 

  apply(dogs, &Gromit::sleep, 'z', 3.0); 

  apply(dogs, &Gromit::sit); 

  } ///:~ 

  尽管apply的定义有些复杂,新手未必能完全理解,但它的使用简洁明了,新手也知道该怎么用,我想,这就是那些为自己

的程序而奋斗所为达到的目标吧:无需知道细节,只需知道实现自己的目标即可。 

  模板的模板 

  //: C03:TemplateTemplate.cpp 

  #include <vector> 

  #include <iostream> 

  #include <string> 

  using namespace std; 

  // As long as things are simple, 

  // this approach works fine: 

  template<typename C> 

  void print1(C& c) 

  { 

  typename C::iterator it; 

  for(it = c.begin(); it != c.end(); it++) 

  cout << *it << " "

  cout << endl; 

  } 

  // Template-template argument must 

  // be a class; cannot use typename: 

  template<typename T, template<typename> class C> 

  void print2(C<T>& c) 

  { 

  copy(c.begin(), c.end(), 

  ostream_iterator<T>(cout, " ")); 

  cout << endl; 

  } 

  int main() 

  { 

  vector<string> v(5, "Yow!"); 

  print1(v); 

  print2(v); 

  } ///:~ 

  成员函数模板 

  事实上,我们也可以把apply()作为一个类中的成员函数模板,这样可以使声明更加清晰:

  dogs.apply(&Gromit::sit); 

  作为容器类中的一个成员,apply()的定义被证明是非常清晰的,为了完成这个,我们需要从现存STL系列容器中继承一个

新的容器,把我们的新函数加到这个容器中去。当然,为了具有最好的适应性,我们将使用STL系列的容器,并且,必须使用模

板的模板来做这项工作,告诉编译器一个模板参数是即上是一个模板,而它自身作为一个类型参数也可以被初始化。看下面: 

  //: C03:applyMember.h 

  // applySequence.h modified to use 

  // member function templates 

  template<class T, template<typename> class Seq> 

  class SequenceWithApply : public Seq<T*> 

  { 

  public: 

  // 0 arguments, any type of return value

  template<class R> 

  void apply(R (T::*f)()) 

  { 

  iterator it = begin(); 

  while(it != end()) 

  { 

  ((*it)->*f)(); 

  it++; 

  } 

  } 

  // 1 argument, any type of return value

  template<class R, class A> 

  void apply(R(T::*f)(A), A a) 

  { 

  iterator it = begin(); 

  while(it != end()) 

  { 

  ((*it)->*f)(a); 

  it++; 

  } 

  } 

  // 2 arguments, any type of return value

  template<class R, class A1, class A2> 

  void apply(R(T::*f)(A1, A2), 

  A1 a1, A2 a2) 

  { 

  iterator it = begin(); 

  while(it != end()) 

  { 

  ((*it)->*f)(a1, a2); 

  it++; 

  } 

  } 

  }; ///:~ 

  因为他们是类的成员,所以apply()函数就不需要那么多参数了,并且iterator所属类也不需要被特殊指定。当然,begin

()和end()也是新类的成员函数了,这一切看上去都那么清晰,明了。然而,基本的代码仍是一样的

  //: C03:applyGromit2.cpp 

  // Test applyMember.h 

  #include "Gromit.h" 

  #include "applyMember.h" 

  #include <vector> 

  #include <iostream> 

  using namespace std; 

  int main() 

  { 

  SequenceWithApply<Gromit, vector> dogs; 

  for(int i = 0; i < 5; i++) 

  dogs.push_back(new Gromit(i)); 

  dogs.apply(&Gromit::speak, 1); 

  dogs.apply(&Gromit::eat, 2.0f); 

  dogs.apply(&Gromit::sleep, 'z', 3.0); 

  dogs.apply(&Gromit::sit); 

  } ///:~ 

  从概念上讲:你现在是从dogs这个容器中调用apply()这个方法了。

相关文章推荐

c++编译器模板机制剖析

编译器编译原理   gcc 基本概念 什么是 gcc,gcc(Gun Compiler Collection 缩写),最初是是作为 c 语言的编译器(Gun C Co...

剖析C++模板(上)

无类型的模板参数        这里有一个用来产生随机数的类,它可以接受一个的数字,然后通过重载()符号,来产生一个符合要求的随机数。具体代码如下:   //: C03:Urand.h /...
  • JYSG9
  • JYSG9
  • 2012年08月23日 15:16
  • 836

C++顺序表模板练习 以及 剖析易出现的浅拷贝问题

/* C++顺序表模板练习 以及 剖析易出现的浅拷贝问题 */#define _CRT_SECURE_NO_WARNINGS 1#include #include using namespace st...
  • alick97
  • alick97
  • 2016年10月25日 14:01
  • 64

C++ 标准模板库STL 队列 queue 使用方法与应用介绍(一)

queue queue模板类的定义在头文件中。 与stack模板类很相似,queue模板类也需要两个模板参数,一个是元素类型,一个容器类型,元素类型是必要的,容器类型是可选的,默认为deq...

c++模板实现多参数任意传 - 类实现

之前写的有篇文章是用方法实习的变长参数,这边就是用类实现的,更加高大上,这样就可以对不同的实例对象进行处理。  下面是《深入理解c++11》书中的截图 下面是自己实现的代码 //-----...

队列(顺序存储)C++模板实现

队列:一端进行插入,另一端进行删除的sh

C++基础::变量模板(variable template)

既然允许C++模板类(class template)的存在,允许C++函数模板(function template)的存在,也应当允许变量模板(variable template)的存在。 引入变量模...

C++ - 模板(template)中typename的使用方法

模板(template)中typename的使用方法 http://blog.csdn.net/caroline_wendy/article/details/23910709 声明templ...

《C++语言基础》实践参考——复数模板类

返回:贺老师课程教学链接【项目6-复数模板类】    阅读教材例10.1。该例实现了一个复数类,但是美中不足的是,复数类的实部和虚部都固定只能是double型的。可以通过模板类的技术手段,设计Comp...

C++标准库和标准模板库

C++强大的功能来源于其丰富的类库及库函数资源。C++标准库的内容总共在50个标准头文件中定义。在C++开发中,要尽可能地利用标准库完成。这样做的直接好处包括:(1)成本:已经作为标准提供,何苦再花费...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:剖析C++模板(中)
举报原因:
原因补充:

(最多只允许输入30个字)