首页 > 代码库 > 【ThinkingInC++】73、深入理解模板
【ThinkingInC++】73、深入理解模板
第五章 深入理解模板
5.1 模板参数
关于bitset
bitset就是可以存放二进制的容器。
对于bitset的主要操作有:
(constructor)
Construct bitset (public member function) //构造bitset.. 格式bitset<长度> 名字
applicable operators
Bitset operators (functions) //可以直接对bitset容器进行二进制操作,如^,|,~,<<,>>等等
operator[]
Access bit (public member function) //可以用如数组形式的赋值。bitset<4> b; b[0]=1;
set
Set bits (public member function)//默认将容器中所有值赋为1,也可以将特定的位置赋给特定的值
如 bitset<4> b; b.set(); //1111. b.set(2,0) // 1011.
reset
Reset bits (public member function) //默认将容器中所有值赋值为0,也可以将特定位置赋特定的值
flip
Flip bits (public member function)//默认将容器中的数取反,1变0,0变1,也可以将特定位置取反bitset<4>b(string ("0001")); b.file(2); // 0101; b.file(); //1010
to_ulong
Convert to unsigned longinteger (public member function) //将容器的值转化成10进制的数
to_string
Convert to string (public member function) //将容器累的值转为字符串
count
Count bits set (public member function) //统计容器中1的个数
size
Return size (public member function) //容器的大小
test
Return bit value (public member function) //返回每个位置上的数
any
Test if any bit is set (public member function) //容器的值>0返回真,反之。
none
Test if no bit is set (public member function) //和any取反。容器的值==0返回真。反之
表3-7 bitset操作
b.any() | b中是否存在置为1的二进制位? |
b.none() | b中不存在置为1的二进制位吗? |
b.count() | b中置为1的二进制位的个数 |
b.size() | b中二进制位的个数 |
b[pos] | 访问b中在pos处的二进制位 |
b.test(pos) | b中在pos处的二进制位是否为1? |
b.set() | 把b中所有二进制位都置为1 |
b.set(pos) | 把b中在pos处的二进制位置为1 |
b.reset() | 把b中所有二进制位都置为0 |
b.reset(pos) | 把b中在pos处的二进制位置为0 |
b.flip() | 把b中所有二进制位逐位取反 |
b.flip(pos) | 把b中在pos处的二进制位取反 |
b.to_ulong() | 用b中同样的二进制位返回一个unsigned long值 |
os << b | 把b中的位集输出到os流 |
Urand.h
/** * 书本:【ThinkingInC++】 * 功能:关于模板bitset * 时间:2014年10月16日16:28:59 * 作者:cutter_point */ #ifndef URAND_H_INCLUDED #define URAND_H_INCLUDED #include <bitset> #include <cstddef> #include <cstdlib> #include <ctime> using std::size_t; using std::bitset; template<size_t UpperBound> class Urand { bitset<UpperBound> used; public: Urand() { srand(time(0)); } //产生随机数 size_t operator()(); }; template<size_t UpperBound> inline size_t Urand<UpperBound>::operator()() { if(used.count() == UpperBound)//b.count() b中置为1的二进制位的个数 used.reset(); //说明已经满了,里面为1的个数满了,全为1,那么这个让他全部重置为0 size_t newval; while(used[newval = rand()%UpperBound]) ; //找到唯一的数值,就是为假的时候跳出来 used[newval]=true; return newval; } #endif // URAND_H_INCLUDED
UrandTest.cpp
/** * 书本:【ThinkingInC++】 * 功能:关于如何使用Urand.h * 时间:2014年10月16日16:29:21 * 作者:cutter_point */ #include <iostream> #include "Urand.h" using namespace std; int main() { Urand<10> u; for(int i=0 ; i < 20 ; ++i) cout<<u()<<' '; system("pause"); return 0; }
5.1.3 模板类型的模板参数
TempTemp.cpp
/** * 书本:【ThinkingInC++】 * 功能:关于模板类型的模板参数 * 时间:2014年10月16日16:29:58 * 作者:cutter_point */ #include <cstddef> #include <iostream> using namespace std; template<class T> class Array { enum { INIT=10 }; //美剧类型,这是为了避免使用宏 T* data; size_t capacity; size_t count; public: Array() { count=0; data=http://www.mamicode.com/new T[capacity = INIT]; //创建一个T类型的数组>
关于typename的探讨
typename
"
typename
"是一个C++程序设计语言中的关键字。当用于泛型编程时是另一术语"class
"的同义词。[1]这个关键字用于指出模板声明(或定义)中的非独立名称(dependent names)是类型名,而非变量名。以下是对于泛型编程中typename
两种迥然不同的用法的解释。
class
关键字的同义词这是一项C++编程语言的泛型编程(或曰“模板编程”)的功能,
typename
关键字用于引入一个模板参数,例如:// 定义一个返回参数中较大者的通用函数template <typename T>const T& max(const T& x, const T& y){if (y < x){return x;}return y;}这种情况下,
typename
可用另一个等效的关键字class
代替,如下代码片段所示:// 定义一个返回参数中较大者的通用函数template <class T>const T& max(const T& x, const T& y){if (y < x) {return x;}return y;}以上两段代码没有功能上的区别。
类型名指示符
考虑下面的错误代码:
template <typename T>void foo(const T& t){// 声明一个指向某个类型为T::bar的对象的指针T::bar * p;}struct StructWithBarAsType {typedef int bar;};int main() {StructWithBarAsType x;foo(x);}这段代码看起来能通过编译,但是事实上这段代码并不正确。因为编译器并不知道
T::bar
究竟是一个类型的名字还是一个某个变量的名字。究其根本,造成这种歧义的原因在于,编译器不明白T::bar
到底是不是“模板参数的非独立名字”,简称“非独立名字”。[2]注意,任何含有名为“bar
”的项的类T,都可以被当作模板参数传入foo()
函数,包括typedef
类型、枚举类型或者变量等。为了消除歧义,C++语言标准规定:
A name used in a template declaration or definition andthat is dependent on a template-parameter is assumed not to name a type unlessthe applicable name lookup finds a type name or the name is qualified by thekeyword typename.
意即出现上述歧义时,编译器将自动默认bar为一个变量名,而不是类型名。所以上面例子中的代码
T::bar * p
会被解释为乘法,而不是声明p为指向T::bar类型的对象的指针。如果还有另一个名为
StructWithBarAsValue
类型,如下:struct StructWithBarAsValue {int bar;};那么,编译器将以完全不同的方式来解释
T::bar * p的含义。
解决问题的最终办法,就是显式地告诉编译器,
T::bar
是一个类型名。这就必须用typename
关键字,例如:template <typename T>void foo(const T& t){// 声明一个指向某个类型为T::bar的对象的指针typename T::bar * p;}这样,编译器就确定了
T::bar
是一个类型名,p也就自然地被解释为指向T::bar
类型的对象的指针了。
5.4 名称查找问题
FriendScope2.cpp
/** * 书本:【ThinkingInC++】 * 功能:关于模板的友元函数 * 时间:2014年10月16日16:30:49 * 作者:cutter_point */ #include <iostream> using namespace std; //前向声明 template<class T> class Friendly; template<class T> void f(const Friendly<T>&); template<class T> class Friendly { T t; public: Friendly(const T& theT) : t(theT) {} friend void f<> (const Friendly<T>&); //这里注意加一个<>是为了表示这是一个模板 void g() { f(*this); } }; void h() { f(Friendly<int>(1)); } template<class T> void f(const Friendly<T>& fo) { cout<<fo.t<<endl; } int main() { h(); Friendly<int>(2).g(); return 0; }
这里Friendly中的f的声明里的尖括号。这谁必须的,它告诉编译器f是一个模板。否则,编译器就会去寻找一个名为f的普通函数而不会找到他。
Box1.cpp
用模板定义操作符号
/** * 书本:【ThinkingInC++】 * 功能:定义模板的操作符 * 时间:2014年10月16日16:31:13 * 作者:cutter_point */ #include <iostream> using namespace std; //前向声明 template<class T> class Box; template<class T> Box<T> operator+(const Box<T>&, const Box<T>&); template<class T> ostream& operator<<(ostream&, const Box<T>&); //定义 template<class T> class Box { T t; public: Box(const T& theT) : t(theT) {} friend Box operator+<>(const Box<T>&, const Box<T>&); //表明这是一个模板 friend ostream& operator<< <>(ostream&, const Box<T>&); //同上 }; template<class T> Box<T> operator+(const Box<T>& b1, const Box<T>& b2) { return Box<T>(b1.t+b2.t); } template<class T> ostream& operator<<(ostream& os, const Box<T>& b) { return os<<'['<<b.t<<']'; } int main() { Box<int> b1(1), b2(2); cout<<b1+b2<<endl; return 0; }
这个程序有一个缺点,就是不能进行隐式转换,因为模板没有提供这些转换。
【ThinkingInC++】73、深入理解模板