一个有趣的东西:实现一个函数print, 输入一个数组, 输出数组的各个维度长度。

eg.
int a[], b[][], c[][][];
print(a); //(2, 4)
print(b); //(3, 16) (4, 4)
print(c); //(5, 168) (6, 28) (7, 4)
template<typename T>
void print(const T &A) {
printf("\n");
}
template<typename T, int N>
void print(const T (&A)[N]) {
printf("(%d, %d) ", N, sizeof(A[]));
print(A[]);
}

学习版块

https://github.com/wuye9036/CppTemplateTutorial  空明流转

typename与class

在 C++ Template 中很多地方都用到了 typename 与 class 这两个关键字,而且好像可以替换,是不是这两个关键字完全一样呢?
相信学习 C++ 的人对 class 这个关键字都非常明白,class 用于定义类,在模板引入 c++ 后,最初定义模板的方法为:
template<class T>......
这里 class 关键字表明T是一个类型,后来为了避免 class 在这两个地方的使用可能给人带来混淆,所以引入了 typename 这个关键字,它的作用同
class 一样表明后面的符号为一个类型,这样在定义模板的时候就可以使用下面的方式了:
template<typename
T>......
在模板定义语法中关键字 class 与 typename 的作用完全一样。
typename 难道仅仅在模板定义中起作用吗?其实不是这样,typename 另外一个作用为:使用嵌套依赖类型(nested depended name),如下所示:
class MyArray
{
public:
typedef int LengthType;
.....
} template<class T>
void MyMethod( T myarr )
{
typedef typename T::LengthType LengthType;
LengthType length = myarr.GetLength;
}
这个时候 typename 的作用就是告诉 c++ 编译器,typename 后面的字符串为一个类型名称,而不是成员函数或者成员变量,这个时候如果前面没有
typename,编译器没有任何办法知道 T::LengthType 是一个类型还是一个成员名称(静态数据成员或者静态函数),所以编译不能够通过。

eg1.

 template <typename T> struct X {};
template <typename T> struct Y
{
// X可以查找到原型;
// X<T>是一个依赖性名称,模板定义阶段并不管X<T>是不是正确的。
typedef X<T> ReboundType; // X可以查找到原型;
// X<T>是一个依赖性名称,X<T>::MemberType也是一个依赖性名称;
// 所以模板声明时也不会管X模板里面有没有MemberType这回事。
typedef typename X<T>::MemberType MemberType2; // UnknownType 不是一个依赖性名称
// 而且这个名字在当前作用域中不存在,所以直接报错。
// typedef UnknownType MemberType3; void foo()
{
X<T> instance0;
typename X<T>::MemberType instance1;
}
};

eg2.

 struct A;
template <typename T> struct B;
template <typename T> struct X {
typedef X<T> _A; // 编译器当然知道 X<T> 是一个类型。
typedef X _B; // X 等价于 X<T> 的缩写
typedef T _C; // T 不是一个类型还玩毛 // !!!注意我要变形了!!!
class Y {
typedef X<T> _D; // X 的内部,既然外部高枕无忧,内部更不用说了
typedef X<T>::Y _E; // 嗯,这里也没问题,编译器知道Y就是当前的类型,
// 这里在VS2015上会有错,需要添加 typename,
// Clang 上顺利通过。
typedef typename X<T*>::Y _F; // 这个居然要加 typename!
// 因为,X<T*>和X<T>不一样哦,
// 它可能会在实例化的时候被别的偏特化给抢过去实现了。
}; typedef A _G; // 嗯,没问题,A在外面声明啦
typedef B<T> _H; // B<T>也是一个类型
typedef typename B<T>::type _I; // 嗯,因为不知道B<T>::type的信息,
// 所以需要typename
//typedef B<int>::type _J; // B<int> 不依赖模板参数,
// 所以编译器直接就实例化(instantiate)了
// 但是这个时候,B并没有被实现,所以就出错了
};

自己的理解:什么时候需要typename?如果编译器无法判断当前名称代表的是类型还是实例的时候,需要用typename来表示指代类型。如果遇到T::size_type * p; 无法知道是乘法运算还是定义指针。这时候,当我们希望通知编译器一个名字表示类型时,必须用关键字typename,而不是class。则为typename T::size_type *p;

============================分割线============================

引用折叠

引用折叠只能应用于间接创建的引用的引用,如类型别名或模板参数。

& &&,&& &,& &折叠后都是左值引用&; && &&折叠后是右值引用&&。

模板实参推断与引用

其中i是int,ci是const int.

template<typename T> void f1(T&); // 实参必须是左值,const属性得到保持

f1(i); //T是int

f1(ci); //T是const int

f1(5); //error, 必须是左值

template<typename T> void f2(const T&);  //可以接受右值

f2(i);

f2(ci);

f2(5); //以上T均为int

template<typename T> void f3(T&&); //实参为左值,T为左值引用;实参为const左值,T为const左值引用;实参为右值,T为右值引用

f3(i); //T是int&

f3(ci); //T是const int&

f3(5); //T是int&&

T&&,对应的const属性和左值/右值属性将得到保持。T&&可以实现转发,保持转发参数的所有性质,包括const和左右值。

移动与完美转发

std::move 如下.

/// remove_reference
template<typename _Tp>
struct remove_reference
{ typedef _Tp type; }; template<typename _Tp>
struct remove_reference<_Tp&>
{ typedef _Tp type; }; template<typename _Tp>
struct remove_reference<_Tp&&>
{ typedef _Tp type; }; template <typename T>
typename remove_reference<T>::type&& move(T&& t) {
return static_cast<typename remove_reference<T>::type&&>(t);
}

std::forward.

template <typename T>
T&& forward(typename remove_reference<T>::type& param)
{
return static_cast<T&&>(param);
}

eg.

template<typename T>
void foo(T&& fparam)
{
std::forward<T>(fprams);
} int i = ;
foo(i);//T为int&,std::forward<T>(fprams)的类型为int & &&,折叠后为int &
foo();//T为int, std::forward<T>(fprams)的类型为int && //通过完美转发,可以简化代码,其中m_var1和m_var2是全局变量
void set(const string & var1, const string & var2) {
//拷贝赋值
m_var1 = var1;
m_var2 = var2;
}
void set(string && var1, string && var2){
//移动赋值
m_var1 = std::move(var1);//move不能省略,具名变量都被当作左值
m_var2 = std::move(var2);
}
//以上两个函数可以用以下函数代替
template<typename T1, typename T2>
void set(T1 && var1, T2 && var2){
m_var1 = std::forward<T1>(var1);
m_var2 = std::forward<T2>(var2);
}
//注:forward常用于template函数中,必须要多带一个参数forward<T>

可变参数函数模板(variadic function template)

主要使用了包扩展(pack expansion)的方式, 即"...",  把一个模式(pattern)扩展为包中每一个元素(element)的形式;

可变参数函数模板, 常使用递归(recursive)进行处理包(pack)参数,

需要一个非可变参数(nonvariadic)函数模板,结束递归, 当最后一次调用时, 调用非可变参数版本, 则递归结束;

还需要一个绑定(bing)的参数, 处理包(pack)中的一些元素, 通过递归,顺次处理包中的元素;

包扩展可以应用于各种形式, 如

函数的模板参数, 例如: templae<typename... Args>    扩展后即 template<typename Args1, typename Args2, typename Args3>

函数的参数模板, 例如: cosnt Args&... rest 扩展后即const Args& rest1, const Args& rest2, const Args& rest3

函数的形参, 例如: rest... 扩展后即rest1, rest2, rest3

函数模板, 例如: debug_rep(rest)...扩展后即debug_rep(rest1), debug_rep(rest2), debug_rep(rest3) //debug_rep是模板函数名

eg.

 #include <iostream>
#include <sstream> using namespace std; //返回bug信息
template <typename T>
std::string debug_rep (const T& t)
{
std::ostringstream ret;
ret << t;
return ret.str();
} //非可变参数模板
template<typename T>
std::ostream &print(std::ostream &os, const T &t)
{
//std::cout << "This is nonvariadic function! ";
return os << t;
} //可变参数模板, "..."是包扩展(Pack Expansion)
template <typename T, typename... Args>
std::ostream &print(std::ostream &os, const T &t, const Args&... rest)
{
os << t << ", ";
return print(os, rest...);
} //函数模板的包扩展
template <typename... Args>
std::ostream &errorMsg(std::ostream &os, const Args&... rest)
{
return print(os, debug_rep(rest)...); //使用模板的包扩展
} int main()
{
int i(); std::string s("girls");
//print(std::cout, i, s, 42);
errorMsg(std::cout, i, s, , "ladies"); return ;
}

C++11中的tuple就是可变参数类模板,在github找到了一份实现代码。(这个人好像还实现了好多别的东西,比如红黑树,堆排序,哈希表)

 #include <iostream>

 template<typename ... Types> class Tuple;
template<> class Tuple<> {};
template<typename First, typename ... Rest>
class Tuple<First, Rest...>: private Tuple<Rest...> {
First Member;
public:
Tuple(const First& first, const Rest& ... rest):
Tuple<Rest...>(rest...), Member(first) {} const First& Head() const {
return Member;
} const Tuple<Rest...>& Tail() const {
return *this;
}
}; // As a return value of Get<>(Tuple) method is not a reference,
// in order to make it available to return POD types. template<size_t I, class T>
struct TupleElement; template<size_t I, class Head, class ... Rest>
struct TupleElement<I, Tuple<Head, Rest...> >:
public TupleElement<I - , Tuple<Rest...> > {
static typename TupleElement<I, Tuple<Head, Rest...> >::Type
Get(const Tuple<Head, Rest...>& t) {
return TupleElement<I - , Tuple<Rest...> >::Get(t.Tail());
}
}; template<class Head, class ... Rest>
struct TupleElement<, Tuple<Head, Rest...> > {
typedef Head Type;
static typename TupleElement<, Tuple<Head, Rest...> >::Type
Get(const Tuple<Head, Rest...>& t) {
return t.Head();
}
}; template<size_t Pos, class Head, class ... Rest>
typename TupleElement<Pos, Tuple<Head, Rest...> >::Type
Get(const Tuple<Head, Rest...>& t) {
return TupleElement<Pos, Tuple<Head, Rest...> >::Get(t);
} int main() {
Tuple<int, double, char> t(, 3.14, 'a');
std::cout << Get<>(t) << std::endl;
std::cout << Get<>(t) << std::endl;
std::cout << Get<>(t) << std::endl;
return ;
}

转发参数包

组合使用forward机制与可变参数模板实现转发参数包。《C++ primer 5th》16.4.3

============================分割线============================

模板元编程

(黑魔法:编译期间完成计算,如斐波那契,平方根等)

eg.斐波那契等

 #include <bits/stdc++.h>

 template<int N>
struct Fac{
static int const result = Fac<N->::result + Fac<N->::result;
};
template<>
struct Fac<>{
static int const result = ;
};
template<>
struct Fac<>{
static int const result = ;
};
/*
推荐使用enum
静态成员变量只能是左值
如果遇到void foo(int const&);
foo(Fac<7>::result);
编译器将传递Fac<7>::result的地址,
会强制编译期实例化静态成员的定义,
并为该定义分配内存,
于是该计算将不局限于完全的“编译期”效果
而enum不是左值, 会以常量的形式传递参数
以上抄自<<C++ template(中文版)>>P296
*/ template<int N>
struct Sum{
enum{result = N+Sum<N->::result};
};
template<>
struct Sum<>{
enum{result = };
}; int main(){
std::cout << Fac<>::result << std::endl;
//std::cout << Fac<47>::result << std::endl; // without Fac<46>, compile error
std::cout << Sum<>::result << std::endl;
//std:cout << Sum<901>::result << std::endl; // without Sum<900>, compile error
std::cout << Sum<>::result << std::endl; // OK
//std::cout << Sum<1801>::result << std::endl; // without Sum<1800>, compile error
return ;
}

eg. Sqrt简易版本

 //easy版本, :?条件表达式会同时实例化Sqrt<20, 0, 9>, Sqrt<20, 10, 20>
#include <bits/stdc++.h>
template<int N, int L = , int R = N>
class Sqrt{
public:
enum{m = (L+R+) >> };
enum{result = m*m > N? Sqrt<N, L, m->::result : Sqrt<N, m, R>::result};
};
template<int N, int L>
class Sqrt<N, L, L>{
public:
enum{result = L};
}; int main(){
std::cout << Sqrt<>::result;
return ;
}

tips:可以通过typedef+IfThenElse模板优化条件表达式

 #include <bits/stdc++.h>

 #ifndef IFTHENELSE
#define IFTHENELSE
template<bool C, typename Ta, typename Tb>
class IfThenElse; //局部特化
template<typename Ta, typename Tb>
class IfThenElse<true, Ta, Tb>{
public:
typedef Ta Result;
};
template<typename Ta, typename Tb>
class IfThenElse<false, Ta, Tb>{
public:
typedef Tb Result;
};
#endif template<int N, int L = , int R = N>
class Sqrt{
public:
enum{m = (L+R+) >> };
typedef typename IfThenElse<
(m*m > N),
Sqrt<N, L, m->,
Sqrt<N, m, R>
>::Result SubT; //定义typedef不会实例化
enum{result = SubT::result};
};
template<int N, int L>
class Sqrt<N, L, L>{
public:
enum{result = L};
}; int main(){
std::cout << Sqrt<>::result;
return ;
}

迭代版本

 #include <bits/stdc++.h>

 #ifndef IFTHENELSE
#define IFTHENELSE
template<bool C, typename Ta, typename Tb>
class IfThenElse; //局部特化
template<typename Ta, typename Tb>
class IfThenElse<true, Ta, Tb>{
public:
typedef Ta Result;
};
template<typename Ta, typename Tb>
class IfThenElse<false, Ta, Tb>{
public:
typedef Tb Result;
};
#endif template<int N>
class Value{
public:
enum{result = N};
};
template<int N, int I = >
class Sqrt{
public:
typedef typename IfThenElse<
((I+)*(I+) > N),
Value<I>, //i, 特意构造一个Value类
Sqrt<N, I+>
>::Result SubT; enum{result = SubT::result};
}; int main(){
std::cout << Sqrt<>::result;
return ;
}

未完待续

C++模板学习笔记的更多相关文章

  1. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  2. 初步C++类模板学习笔记

    类模板 实现:在上课时间的定义给它的一个或多个参数,这些参数代表了不同的数据类型.                              -->抽象的类. 在调用类模板时, 指定參数, 由编 ...

  3. tornada模板学习笔记

    import tornado.web import tornado.httpserver import tornado.ioloop import tornado.options import os. ...

  4. 《C++ Primer Plus》14.4 类模板 学习笔记

    14.4.1 定义类模板下面以第10章的Stack类为基础来建立模板.原来的类声明如下:typedef unsigned long Item; class Stack{private:    enum ...

  5. jTemplates模板学习笔记

    1.jTemplates工作方式   1)setTemplateElement:指定可处理的模板对象 2)processTemplate:对模板化的对象进行数据处理 2.语法解析   1)jTempl ...

  6. bzoj 2618 半平面交模板+学习笔记

    题目大意 给你n个凸多边形,求多边形的交的面积 分析 题意\(=\)给你一堆边,让你求半平面交的面积 做法 半平面交模板 1.定义半平面为向量的左侧 2.将所有向量的起点放到一个中心,以中心参照进行逆 ...

  7. [原创]java WEB学习笔记109:Spring学习---spring对JDBC的支持:使用 JdbcTemplate 查询数据库,简化 JDBC 模板查询,在 JDBC 模板中使用具名参数两种实现

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  8. Yii框架学习笔记(二)将html前端模板整合到框架中

    选择Yii 2.0版本框架的7个理由 http://blog.chedushi.com/archives/8988 刚接触Yii谈一下对Yii框架的看法和感受 http://bbs.csdn.net/ ...

  9. NVelocity模板引擎学习笔记

    NVelocity模板引擎学习笔记 学习模板引擎有一段时间现在做一些总结

随机推荐

  1. mac 下删除行末^M 字符

    在vi 打开文件模式下进行字符替换 :%s/^M/\r/g   //这里的^M是同时按ctrl+v+m获得的,否则会显示找不到^M

  2. Elasticsearch的停用词(stopwords)

    1.问题 在使用搜索引擎(Elasticsearch或Solr)作为应用的后台搜索平台的时候,会遇到停用词(stopwords)的问题. 在信息检索中,停用词是为节省存储空间和提高搜索效率,处理文本时 ...

  3. linux运维升级路线

    运维工程师是从一个呆逼进化为苦逼再成长为牛逼的过程,前提在于你要能忍能干能拼,还要具有敏锐的嗅觉感知前方潮流变化.如:今年大数据,人工智能比较火……(相对表示就是 Python 比较火) 之前写过运维 ...

  4. python爬虫项目(新手教程)之知乎(requests方式)

    -前言 之前一直用scrapy与urllib姿势爬取数据,最近使用requests感觉还不错,这次希望通过对知乎数据的爬取为 各位爬虫爱好者和初学者更好的了解爬虫制作的准备过程以及requests请求 ...

  5. [java] jar file

    查看 .jar 内的文件 jar tf jarfile.jar maven 项目中, java 读取目标文件 运行 mvn package 打包项目是, src/main/resources 下的文件 ...

  6. java工程文件路径的问题

    String classpath = this.getClass().getResource("/").getPath().replaceFirst("/WEB-INF/ ...

  7. Daily Scrumming 2015.10.22(Day 3)

    今明两天任务表 Member Today’s Task Tomorrow’s Task 江昊 学习rails ActiveRecord 购买.注册域名 继续学习rails ActiveRecord 数 ...

  8. Alpha版会议总结

    目前的进度: 实现了文字备忘的录入: 实现了提醒功能: 实现了可视化界面: 语音录入功能还没有完成: 界面相当粗糙: 遇到的问题: 语音录入按钮按下后没有反应,目前没有解决思路和方法. 原本的解屏功能 ...

  9. Leetcode题库——35.搜索插入位置

    @author: ZZQ @software: PyCharm @file: searchInsert.py @time: 2018/11/07 19:20 要求:给定一个排序数组和一个目标值,在数组 ...

  10. Reaction to 构造之法 of Software Engineering From The First Chapter toThe Fifth Chapter

    几个星期前,我阅读过一篇文章,一位老师教导自己的学生要积极地去阅读文学文献,其中,我很欣赏他的一句话:“Just think of liturature as if you're reading a ...