远观像"类",近看不是

Template是编译时多态。所有的模板都是在编译时产生对应的代码,它没有面向对象中的虚表,无法实现动态多态。

模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。

Function Template

A function template is a prescription for the compiler to generate particular instances of a function varying by type.

”变量类型“的部分放在一块,与“变量类型无关”的部分放在另一边。

放在"头文件“中,声明和实现建议这么做。其他方式不推荐。

模板函数

typename的使用,增强了函数的扩展性。

#include <iostream>
#include <string> using namespace std;
-------------------------------------------------------------------
template <typename T>
inline T const& Max (T const& a, T const& b)
{
return a < b ? b:a;
}
-------------------------------------------------------------------
int main ()
{ int i = ;
int j = ;
cout << "Max(i, j): " << Max(i, j) << endl; double f1 = 13.5;
double f2 = 20.7;
cout << "Max(f1, f2): " << Max(f1, f2) << endl; string s1 = "Hello";
string s2 = "World";
cout << "Max(s1, s2): " << Max(s1, s2) << endl; return ;
}

Name Resolution

概念解析:

Ref: Templates and Name Resolution

中文版链接:模板和名称解析

在模板定义中,有三种类型的名称。

      • 本部声明的名称,包括模板本身的名称以及在模板定义中声明的任何名称。

      • 模板定义之外的封闭范围中的名称。

      • 在某种程度上依赖于模板自变量的名称 (称为依赖名称) 。

参考:《C++语言程序设计(第4版)》(郑莉,董渊)

    • designer & client
    • Only three kinds of implicit conversions for type parameters:

参数的类型不同的数字比较的话,例如:int , double

使用例如:min(static_cast<double> i, d)的方式。

decltype: what's its type?

decltype 与 auto

Ref: auto和decltype的用法总结

Ref: auto和decltype的用法总结【有赞】

在使用过程中和const、引用和指针结合时需要特别小心。decltype和auto都可以用来推断类型,但是二者有几处明显的差异:

    1. auto忽略顶层const,decltype保留顶层const;
    2. 对引用操作,auto推断出原有类型,decltype推断出引用;【decltype更牛逼一些】
    3. 对解引用操作,auto推断出原有类型,decltype推断出引用;
    4. auto推断时会实际执行,decltype不会执行,只做分析。

(1) decltype 的作用

decltype只是为了推断出表达式的类型而不用这个表达式的值来初始化对象.

表达式:
decltype(func()) sum = x; // sum的类型是函数func()的返回值的类型, 但是这时不会实际调用函数func() 变量:
int i = ;
decltype(i) j = ; // i的类型是int, 所以j的类型也是int

(2) decltype 和 const

不论是顶层const还是底层const, decltype都会保留

const int i = ;
decltype(i) j = i; // j的类型和i是一样的, 都是const int

作为对比,auto的特性则是:

auto会忽略掉顶层const,保留底层const. 举例:

const int i = ;
auto a = i;      // 变量i是顶层const, 会被忽略, 所以b的类型是int
auto b = &i;     // 变量i是一个常量, 对常量取地址是一种底层const, 所以b的类型是const int *

因此,如果希望推断出的类型是顶层const的,那么就需要在auto前面加上cosnt:

const auto c = i;

(3) decltype 和 引用

① 如果表达式是引用类型, 那么decltype的类型也是引用

const int i = , &j = i;
decltype(j) k = ;       // k的类型是 const int &

② 如果表达式是引用类型, 但是想要得到这个引用所指向的类型, 需要修改表达式: 【找引用指代的原类型】

int i = , &r = i;
decltype(r + ) t = ;     // 此时是int类型

③ 对指针的解引用操作返回的是引用类型【找指针指代的原类型】

int i = , j = , *p = &i;
decltype(*p) c = j;       // c是int类型的引用, c和j绑定在一起

④ 如果一个表达式的类型不是引用, 但是我们需要推断出引用, 那么可以加上一对括号, 就变成了引用类型了【凭空添加一个原变量的引用类型】

int i = ;
decltype((i)) j = i;      // 此时j的类型是int类型的引用, j和i绑定在了一起

为啥用 decltype

auto 和 decltype 都是编译时类型推导。

auto 解决不了参数type不定的问题

auto means "the variable's type is deduced from the initialiser."

decltype refers to a type in an arbitrary context.

Here's an example where you can't use auto:

template <typename T, typename U, typename V>
void madd(const T &t, const U &u, const V &v, decltype(t * u + v) &res)
{
res = t * u + v;
}

(1) 解决模板函数返回值不确定情况:

template <typename T, typename U>
auto
mymin(T a, U b) -> decltype(a < b ? a : b) {
return a < b ? a: b;
}

泛型编程中结合auto,用于追踪函数的返回值类型!

(2) 根据变量推导齐类型:

int i;
const &j = i;
int *p = i;
int k; decltype(i) x;         // int x: i is a variable
decltype(j) y = k;     // int &y = k: j is an lvalue
decltype(*p) z = k; // int &z = k: *p is an lvalue
decltype((i)) w = k;     // int &w = k: (i) is an lvalue

(3) 与using/typedef合用,用于定义类型:

Ref: C++11特性:decltype关键字

using size_t    = decltype(sizeof());//sizeof(a)的返回值为size_t类型
using ptrdiff_t = decltype((int*) - (int*));
using nullptr_t = decltype(nullptr);
vector<int >vec;
typedef decltype(vec.begin()) vectype;
for (vectype i = vec.begin; i != vec.end(); i++)
{
//...
}

 

(4) 重用匿名类型:

在C++中,我们有时候会遇上一些匿名类型,而借助decltype,我们可以重新使用这个匿名的结构体:

struct
{
int d ;
doubel b;
}anon_s;
decltype(anon_s) as ;   //定义了一个上面匿名的结构体

decltype推导四规则

推导规则

    1. 如果e是一个没有带括号的标记符表达式或者类成员访问表达式,那么的decltype(e)就是e所命名的实体的类型。此外,如果e是一个被重载的函数,则会导致编译错误。
    2. 否则 ,假设e的类型是T,如果e是一个将亡值,那么decltype(e)为T&&
    3. 否则,假设e的类型是T,如果e是一个左值,那么decltype(e)为T&。
    4. 否则,假设e的类型是T,则decltype(e)为T。 
int decipline(void)
{ int i = ;
int arr[] = { };
int *ptr = arr;
struct S{ double d; }s ; // void Overloaded(int);
// void Overloaded(char);//重载的函数 int && RvalRef();
const bool Func(int); //规则一:推导为其类型
decltype (arr) var1; //int 标记符表达式【又是一个数组的bug】
decltype (ptr) var2; //int * 标记符表达式
decltype (s.d) var3; //doubel 成员访问表达式 //decltype(Overloaded) var4;//重载函数。编译错误。 //规则二:将亡值。推导为类型的右值引用。
decltype (RvalRef()) var5 = ; //规则三:左值,推导为类型的引用。
decltype ((i))var6 = i; //int& decltype (true ? i : i) var7 = i; //int& 条件表达式返回左值。
{
int x = ;
float y = 1.11; decltype(true?x:y) c = (false?x:y);
cout << "c = " << c << endl;
} decltype (++i) var8 = i; // int& ++i返回i的左值。
decltype(arr[]) var9 = i; // int&. []操作返回左值
decltype(*ptr) var10 = i; // int& *操作返回左值
decltype("hello")var11 = "hello"; // const char(&)[9] 字符串字面常量为左值,且为const左值。 //规则四:以上都不是,则推导为本类型
decltype() var12;//const int
decltype(Func()) var13=true;//const bool
decltype(i++) var14 = i;//int i++返回右值 return ;
}

左值判断

cout << is_lvalue_reference<decltype(++i)>::value << endl;

模板的健壮性

(1) 若要比较字符串,使用函数override.

const char *s = "xyz";
const char *t = "abc"; /* 返回了数字,认为是地址,也就是数组的首地址 */
cout << min(s, t) << endl;

(2) 模板的设计 更加健壮。如下:

(3) template function的默认参数 也可以设置,实现健壮性:

(4) template's variadic function ,进一步健壮:

注意关键写法:

template <typename A, typename... B>
void print(A head, B... tail) {
...
}

Class Template

  ---- 类是不能重载的,要解决这个问题。

菜鸡教程

  • 作为对比:函数模板
template <typename T>
inline T const& Max (T const& a, T const& b)
{
return a < b ? b:a;
}
  • 类的模板
using namespace std;

template <typename> class Stack;
template <typename T> ostream& operator << (ostream &os, const Stack<T>&s); template <typename T> class Stack {
public:
  friend ostream& operator << <T>(ostream &, const Stack<T> &);  //指明:是以上实现函数模板的实例
  void push(const T &);
  void pop();
  T& top();
  const T& top() const;
  bool empty() const;
private:
  vector<T> stack_;     // 同时也表明了:在已有的类型的基础上 再扩展出一个类型。更加灵活的方式可使用参数法传入。
};
  • 菜鸡实例

Ref: http://www.runoob.com/cplusplus/cpp-templates.html

一个栈的“类”,但不知道未来的元素可能会是什么类型,遂使用“模板”。

#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept> using namespace std; template <class T>  // 【定义模板】
class Stack {
private:
vector<T> elems; // 元素 public:
void push(T const&); // 入栈
void pop(); // 出栈
T top() const; // 返回栈顶元素
bool empty() const{ // 如果为空则返回真。
return elems.empty();
}
};
----------------------------------------------------------------
template <class T>
void Stack<T>::push (T const& elem)
{
// 追加传入元素的副本
elems.push_back(elem);
} template <class T>
void Stack<T>::pop ()
{
if (elems.empty()) {
throw out_of_range("Stack<>::pop(): empty stack");
}
// 删除最后一个元素
elems.pop_back();
} template <class T>
T
Stack<T>::top () const
{
if (elems.empty()) {
throw out_of_range("Stack<>::top(): empty stack");
}
// 返回最后一个元素的副本
return elems.back();
}
----------------------------------------------------------------
int main()
{
try {
Stack<int> intStack; // int 类型的栈 【使用模板】
Stack<string> stringStack; // string 类型的栈 // 操作 int 类型的栈
intStack.push();
cout << intStack.top() <<endl; // 操作 string 类型的栈
stringStack.push("hello");
cout << stringStack.top() << std::endl;
stringStack.pop();
stringStack.pop();
}
catch (exception const& ex) {
cerr << "Exception: " << ex.what() <<endl;
return -;
}
}

COMP6771 教程

模板的高一个层次的视角去看待这个问题:std::deque<T>

在头文件中实现:

友元相关的部分:

template <typename T>
std::ostream& operator<<(std::ostream &os, const Stack<T> &s) {
for (unsigned int i = ; i < s.stack_.size(); i++) {
os << s.stack_[i] << " ";
}
return os;
}
//---test.h

#ifndef test_h_
#define test_h_
#include <iostream>
using namespace std; // 改动一:增加函数模板的声明——而这又需要先声明类模板
template <class T> class Test;
template <class T>
void display(Test<T> &t); template <class T>
class Test
{
private:
T x;
public:
Test (T x_): x(x_) {}
friend void display<>(Test<T> &t);
// 改动二:在函数名后面加上<>,指明它是之前声明的函数模板 的实例
}; template <class T>
void display(Test<T> &t)
{
cout << t.x << endl;
} #endif // test_h_

View:http://bbs.csdn.net/topics/391867303

cpp中的使用:

// 类模板
Stack<int> s1; // int: template argument

知无涯之C++ typename的起源与用法

也许你会想到上面这段代码中的typename换成class也一样可以,不错!那么这里便有了疑问,这两种方式有区别么?

查看C++ Primer之后,发现两者完全一样。那么为什么C++要同时支持这两种方式呢?

既然class很早就已经有了,为什么还要引入typename这一关键字呢?

问的好,这里面有一段鲜为人知的历史(也许只是我不知道:-))。带着这些疑问,我们开始探寻之旅。Goto: 知无涯之C++ typename的起源与用法

问题浮现

在类作用域一节中,我们介绍了三种名称,由于MyClass已经是一个完整的定义,因此编译期它的类型就可以确定下来,也就是说MyClass::A这些名称对于编译器来说也是已知的。

可是,如果是像T::iterator这样呢?T是模板中的类型参数,它只有等到模板实例化时才会知道是哪种类型,更不用说内部的iterator。通过前面类作用域一节的介绍,我们可以知道,T::iterator实际上可以是以下三种中的任何一种类型:

      • 静态数据成员
      • 静态成员函数
      • 嵌套类型
template <class T>
void foo() {
typename T::iterator * iter;
// ...
}

类模板遇上static关键字

马杀特小哥的简洁文字:c++类模板遇上static关键字

在c++中,我们不能把静态成员放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化,如下面的实例所示。

普通的类 的情况

不需要实例化对象就可以使用,而且类的所有对象都共享同一个静态类成员,因为类静态成员是属于类而不是对象。

include <iostream>

using namespace std;

class Obj{

public:
// 声明类静态成员,这里不能对它进行初始化赋值
static int m_a;
}; // 初始化类静态成员(通过范围解析符::)
int Obj::m_a = ; int main(){
// 通过对象访问静态属性
Obj o;
o.m_a = ;
cout << o.m_a << endl; // 通过类访问静态属性
cout << Obj::m_a <<endl;
}

类模板遇到static

类模板的实现机制是通过二次编译原理实现的。c++编译器并不是在第一个编译类模板的时候就把所有可能出现的类型都分别编译出对应的类(太多组合了),

而是在第一个编译的时候编译一部分,遇到泛型不会替换成具体的类型(这个时候编译器还不知道具体的类型),而是在第二次编译的时候再将泛型替换成具体的类型(这个时候编译器知道了具体的类型了)。

由于类模板的二次编译原理再加上static关键字修饰的成员,当它们在一起的时候实际上一个类模板会被编译成多个具体类型的类,

所以,不同类型的类模板对应的static成员也是不同的(不同的类),但相同类型的类模板的static成员是共享的(同一个类)。

#include <iostream>
using namespace std; template<typename T>
class Obj{
public:
static T m_t;
}; template<typename T>
T Obj<T>::m_t = ;

int main04(){
Obj<int> i1,i2,i3;
i1.m_t = ;
i2.m_t++;
i3.m_t++;
cout << Obj<int>::m_t<<endl;
------------------------------------------
Obj<float> f1,f2,f3;
f1.m_t = ;
f2.m_t++;
f3.m_t++;
cout << Obj<float>::m_t<<endl;
-------------------------------------------
Obj<char> c1,c2,c3;
c1.m_t = 'a';
c2.m_t++;
c3.m_t++;
cout << Obj<char>::m_t<<endl;
}

Member Template

---- 例如:list想使用vector的数据初始化

模板类的模板成员函数

template <typename T, typename CONT = std::deque<T>> class Stack {
  // addition declarations:
  Stack() {} // must define default constructor   // template function inside template class.
  template <typename T2, typename CONT2>
  Stack(const Stack<T2, CONT2> &);   template <typename T2, typename CONT2>
  Stack(Stack<T2, CONT2> &&);   template <typename Iter>
  Stack(Iter b, Iter e);
  template <typename T2, typename CONT2>
  Stack& operator=(const Stack<T2,CONT2> &);
  template <typename T2, typename CONT2>
  Stack& operator=(Stack<T2,CONT2> &&);
  template <typename Iter>
  void assign(Iter b, Iter e);
}

Implementation

(构建func etc.)

如何使用? Client Code

float a[] = {1.1, 2.2, 3.3};
Stack<float> fs(a, a+); //注:使用deque实现的Stack.

End.

[c++] Templates的更多相关文章

  1. 解决Windows版Git出现templates not found的问题

    环境: Win10 x64 Git windows客户端(下载自 https://git-scm.com/) SourceTree 1.9.6.1(使用系统安装的Git,而非SourceTree内嵌的 ...

  2. webstrom live templates

    javascript: 在live templates底部要选择javascript # $('#$END$') $ $($end$) $bd $(document.body) $d $(docume ...

  3. Myeclipse Templates详解(一) —— Java模板基础

    目录 Templates简介 MyEclipse自带Templates详解 新建Template 自定义Template 因为自己比较懒,尤其是对敲重复代码比较厌恶,所以经常喜欢用快捷键和模板,Mye ...

  4. Using FreeMarker templates (FTL)- Tutorial

    Lars Vogel, (c) 2012, 2016 vogella GmbHVersion 1.4,06.10.2016 Table of Contents 1. Introduction to F ...

  5. juqery模板 Templates

    现在已经有了许多JavaScript的解决方案模板,从这方面说,标准化的模板解决方案必然是大势所趋.在本节中,我们向你简要描述四个最流行最有趣的模板.现有的模板解决方案能解决什么?那些特色在jQuer ...

  6. django TEMPLATES

    ?: (1_8.W001) The standalone TEMPLATE_* settings were deprecated in Django 1.8 and the TEMPLATES dic ...

  7. django复习笔记3:urls/views/templates三板斧

    0.先看看文件结构 mysite/ mysite/ ├── __pycache__ │   └── manage.cpython-.pyc ├── blog │   ├── __init__.py │ ...

  8. Effective C++ -----条款44:将与参数无关的代码抽离templates

    Templates生成多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系. 因非类型模板参数(non-type template para ...

  9. 第三节:视图(Views)和模板(Templates)

    目录 概览 编写视图 编辑视图实际做一些事情 抛出404异常 使用模板系统 移除在代码中的硬编码网址 Url名称的命名空间 概览 视图是Django应用的网页的“类型”,一般服务于特定的功能并且有特定 ...

随机推荐

  1. PHP:( && )逻辑与运算符使用说明

    第一次看到以下语句的写法大惑不解 ($mCfg['LockChinaIp']==1 && (int)$_SESSION['AdminUserId']==0 && sub ...

  2. BJITJobs : 北京IT圈高端职位招聘信息,成功率最高的高端求职渠道

    你有实力,但比你差的人都升了,你的师弟都年薪50万了,你还是找不到机会.为什么你离高端机会总是差一步呢?其实你离成功就差一次机会,一个适合你的高端职位的信息! 招聘网站不靠谱:三大网站都是低端职位为主 ...

  3. Python之路【第一篇】python基础

    一.python开发 1.开发: 1)高级语言:python .Java .PHP. C#  Go ruby  c++  ===>字节码 2)低级语言:c .汇编 2.语言之间的对比: 1)py ...

  4. python学习笔记--基础概要

    1.python的int类型没有大小限制(或者说只受机器内存限制),str类型用单引号或者双引号都行,只要对称就可以了.(注意固定性) 2.使用[]表示存取字符串等序列的某一项(索引从0开始) 3.类 ...

  5. ECMAScript6 初步认识

    JavaScript由3部分组成,分别是:Dom,BOM和ECMAScript !核心(ECMAScript):由ECMA-262定义,提供核心语言功能:ECMAScript是属于国际标准化语言,所有 ...

  6. C++指针类型识别正确姿势

    指针是C和C++中编程最复杂也是最有技巧的部分,但对于新手来说,指针无疑是最致命的,让很多人望而退步.不过很多事情都是从陌生开始,然后渐渐熟悉起来的,就像交朋友一样,得花点时间去培养感情才行.不过指针 ...

  7. java学习之面向对象(2)

    在描述事物的时候,该事物已存在就具备的一些内容,这是我们就可以把它们都定义在构造函数中,那么什么是构造函数呢? 构造函数就是构建创造对象时调用的函数,它可以给对象进行初始化.构造函数与类名相同,第一个 ...

  8. "SQL Server does not handle comparison of NText, Text, Xml, or Image data types."

    "SQL Server does not handle comparison of NText, Text, Xml, or Image data types." sql2000 ...

  9. Amoeba -- 阿里巴巴工程师的开源项目之一陈思儒

    http://www.kuqin.com/opensource/20081023/24026.html 个人博客 http://dbanotes.net/web/page/44 阿里巴巴分布式服务框架 ...

  10. [译]Asp.net MVC 之 Contorllers(一)

    Asp.net MVC contorllers 在Ajax全面开花的时代,ASP.NET Web Forms 开始慢慢变得落后.有人说,Ajax已经给了Asp.net致命一击.Ajax使越来越多的控制 ...