深入理解模板

深入模板

一、相关日志

1、模板介绍(Introduction to Templates)

http://blog.163.com/zhoumhan_0351/blog/static/399542272010321103820560

2、C++基本概念(内联,模板,函数)

http://blog.163.com/zhoumhan_0351/blog/static/39954227201002202430247

二、深入模板

    一个无类型模板参数必须是一个编译时所知的整数。

template<class T, size_t N> class Stack {

  T data[N];  // Fixed capacity is N

  size_t count;

public:

  void push(const T& t);

  // Etc.

};

Stack<int, 100> myFixedStack;

由于N的值在编译时是已知的,内含的数据可以被置于进行时堆栈而不是动态存储空间。

在类模板中,可以为模板参数提供默认参数,但是在函数模板中不行。如

template<class T, class Allocator = allocator<T> >

class vector;

任何时候缺省了第二个参数,则用默认的标准allocator模板进行分配。

尽管不能在函数模板中使用默认的模板参数,却能够用模板参数作为普通函数的默认参数。如

#include <iostream>

using namespace std;

template<class T> 

T sum(T* b, T* e, T init = T()) {

  while(b != e)

    init += *b++;

  return init;

}

int main() {

  int a[] = { 1, 2, 3 };

  cout << sum(a, a + sizeof a / sizeof a[0]) << endl; // 6

} ///:~

1、模板类型的模板参数

template<class T, template<class U> class Seq>

其中的U可以省略。

2、When the compiler looks at the inner parameters of a template template 

parameter, default arguments are not considered, so you have to repeat 

the defaults in order to get an exact match.This is the only exception to 

the rule stated earlier that default arguments should appear only once in a 

compilation unit. 

#include <cstddef>

#include <iostream>

using namespace std;

template<class T, size_t N = 10>  // A default argument

class Array {

  T data[N];

  size_t count;

public:

  Array() { count = 0; }

  void push_back(const T& t) {

    if(count < N)

      data[count++] = t;

  }

  void pop_back() {

    if(count > 0)

      --count;

  }

  T* begin() { return data; }

  T* end() { return data + count; }

};

template<class T, template<class, size_t = 10> class Seq>

class Container {

  Seq<T> seq;  // Default used

public:

  void append(const T& t) { seq.push_back(t); }

  T* begin() { return seq.begin(); }

  T* end() { return seq.end(); }

};

int main() {

  Container<int, Array> container;

  container.append(1);

  container.append(2);

  int* p = container.begin();

  while(p != container.end())

    cout << *p++ << endl;

} ///:~

3、The default behavior of the compiler is to assume that a name is not a 

type, you must use typename for nested names (except in constructor 

initializer lists, where it is neither needed nor allowed).

    if a type referred to inside template code is qualified by a template type parameter, you must use the typename keyword as a prefix, unless it appears in a base class specification or initializer list in the same scope (in which case you must not).

for(typename Seq<T>::iterator b = seq.begin();//说明iterator是一个类型,不然编译器会把它作为一个静态数据成员

       b != seq.end();)

typedef的三种用法

(1)Typedefing a typename//说明下面定义的是一个类型,不是创建了。

typename Seq<T>::iterator It;

(2)可以用来代替class,使模板定义中的参数列表意义更明确

template<typename T> class X {};

4、'<' '>'可以用为小于,大于号,还可以做实例化时的标识,及模板的界定符,所以在某些情况下,我们也需要明确指出它是什么。

template<class charT, size_t N>

basic_string<charT> bitsetToString(const bitset<N>& bs) {

  return bs. template to_string<charT, char_traits<charT>,

                                allocator<charT> >();

}

如果有明确用template指出,则会把<作为小于号。The template keyword 

used in this context tells the compiler that what follows is the name of a 

template, causing the < character to be interpreted correctly. 

5、成员模板

template<typename T> class complex {

public:

  template<class X> complex(const complex<X>&);

  complex<float> z;

  complex<double> w(z);//T为double,X为float

模板嵌套

template<typename T>

template<typename X>

complex<T>::complex(const complex<X>& c) {/* Body here…*/}

成员模板可以是类,不一定是函数

template<class T> class Outer {

public:

  template<class R> class Inner {

  public:

    void f();

  };

};

注意:Member template functions cannot be declared virtual. Current compiler technology expects to be able to determine the size of a class’s virtual function table when the class is parsed. Allowing virtual member template functions would require knowing all calls to such member functions everywhere in the program ahead of time. This is not feasible, especially for multi-file projects.

6、几个问题

    You must always use angle brackets when instantiating class templates and you must supply all non-default template arguments. However, with function templates you can often omit the template arguments, and default template arguments are not even allowed. 

    对于一个由模板参数来限定类型的函数模板,C++系统不能提供标准转换。No standard conversions are applied for function arguments whose type is specified by a template parameter.

   数组维数没有被作为函数参数类型的一部分进行传递,除非这个参数是指针或引用。

void init2(T (&a)[R][C]) {}

7、函数模板重载

可以用相同的函数名重载函数模板。

template<typename T> const T& min(const T& a, const T& b) {

  return (a < b) ? a : b;

}

const char* min(const char* a, const char* b) {

  return (strcmp(a, b) < 0) ? a : b;

}

cout << min<>(s1, s2) << endl;  //<>来用强制使用模板

cout << min(s1, s2) << endl; //使用重载的那个(第二个),而不是模板

    在使用过和中,寻找最佳匹配,即如果重载了函数模板,且类型匹配,则用重载的函数模板,而不会再去实例化模板。

(1)以一个已知的函数模板地址作为参数

  transform(s.begin(), s.end(), s.begin(), tolower);

但由于tolower有两个版本,可能会问题。解决方法:

法1:

transform(s.begin(),s.end(),s.begin(),

            static_cast<int (*)(int)>(tolower));

利用强制类型转换,表达一个参数的愿望

法2:

明确的语境中使用

法3:

写一个封装的函数模板

transform(s.begin(),s.end(),s.begin(),&strTolower<char>);

(2)函数模板的半有序

An ordering is defined for function templates, which chooses the most specialized template, if such exists. A function template is considered more specialized than another if every possible list of arguments that matches it also matches the other, but not the other way around. 

template<class T> void f(T);

template<class T> void f(T*);

template<class T> void f(const T*);

    如果有一组模板中没有特化程序最高的模板,则会出现二义性。

8、模板特化

(1)显式特化

#include <cstring>

#include <iostream>

using std::strcmp;

using std::cout;

using std::endl;

template<class T> const T& min(const T& a, const T& b) {

  return (a < b) ? a : b;

}

// An explicit specialization of the min template

template<>

const char* const& min<const char*>(const char* const& a,const char* const& b) {

  return (strcmp(a, b) < 0) ? a : b;

}

int main() {

  const char *s2 = "say \"Ni-!\"", *s1 = "knights who";

  cout << min(s1, s2) << endl;//都是调用第二个模板

  cout << min<>(s1, s2) << endl;

} ///:~

template<>说明是一个显式特化。

(2)半特化

只特化其中的一部分。

template<class T, class U> class C {

public:

  void f() { cout << "Primary Template\n"; }

};

template<class U> class C<int, U> {

public:

  void f() { cout << "T == int\n"; }

};

In other words, specialization, and partial specialization in particular, constitute a sort of “overloading” for class templates.

就当选择特化程序最高的模板。

(3)从一个模板派生另外一个模板

template<class T>

class Sortable : public std::vector<T> {

public:

  void sort();

};

You can factor the bulk of the implementation for storing pointer types into a single class by using a combination of full and partial specialization. The key is to fully specialize for void* and then derive all other pointer types from the void* implementation so the common code can be shared.

void* top() const {

    assert(count > 0);

    return data[count-1];

  }

T* top() const { return static_cast<T*>(Base::top()); }

9、名字查找

模板编译分两个阶段进行。

In the first phase, the compiler parses the template definition looking for obvious syntax errors and resolving all the names it can.

    So instantiation is the second phase of template compilation. Here, the compiler determines whether to use an explicit specialization of the template instead of the primary template.

    限定名,是指具有类名前辍,或者是被一个对象名加上点运算符(or ->)修饰。

MyClass::f();

x.f();

p->f();

ADL specifies that when an unqualified function call appears and its declaration is not in (normal) scope, the namespaces of each of its arguments are searched for a matching function declaration.

std::cout << s;

operator<<(std::cout, s);

则在std中寻找一个能匹配这个函数声明的函数

operator<<(std::ostream&, std::string)

可以用圆括号来避开ADL如下

(f)(x, y);  // ADL suppressed

To clarify and summarize: name lookup is done at the point of instantiation if the name is dependent, except that for unqualified dependent names the normal name lookup is also attempted early, at the point of definition. All non-dependent names in templates are looked up early, at the time the template definition is parsed. (If necessary, another lookup occurs at instantiation time, when the type of the actual argument is known.)

关联名称,在定义时是不确定的,只能在实例化时。

10、友员和模板

友员模板提前声明。可以在主类模板中完全的定义友员函数。友员模板也可以出现在非模板类中。

template<class T> class Friendly {

  T t;

public:

  Friendly(const T& theT) : t(theT) {}

  friend void f(const Friendly<T>& fo) {

    cout << fo.t << endl;

  }

  void g() { f(*this); }

};

11、习语

(1)特征

The traits template technique, is a means of bundling type-dependent declarations together. In essence, using traits you can “mix and match” certain types and values with contexts that use them in a flexible manner, while keeping your code readable and maintainable.

    如char_traits等。

template<class charT,

  class traits = char_traits<charT>,

  class allocator = allocator<charT> >

  class basic_string;

Using traits provides two key advantages: (1) it allows flexibility and extensibility in pairing objects with associated attributes or functionality, and (2) it keeps template parameter lists small and readable. 

(2)策略

(3)递归模板模式

class Counted {

  static int count;

public:

  Counted() { ++count; }

  Counted(const Counted&) { ++count; }

  ~Counted() { --count; }

  static int getCount() { return count; }

};

int Counted::count = 0;

class CountedClass : public Counted {};

class CountedClass2 : public Counted {};

12、模板元编程

#include <iostream>

using namespace std;

template<int n> struct Factorial {

  enum { val = Factorial<n-1>::val * n };

};

template<> struct Factorial<0> {

  enum { val = 1 };

};

int main() {

  cout << Factorial<12>::val << endl; // 479001600

} ///:~

有编译时编程(循环,分解,选择,断言等),表达式模板。关于这一部分,可以参见:

1、C++ Templates,David Vandevoorde.

2、C++编程思想,thinking in C++

13、模板编译模型

如包含模型(显式实例化,导出模板)

分离模型(export).

一个模板意味着一个接口。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值