第五章 深入理解模板
5.1 模板参数
关于bitset
bitset就是可以存放二进制的容器。
对于bitset的主要操作有:
Construct bitset (public member function) //构造bitset.. 格式bitset<长度> 名字
Bitset operators (functions) //可以直接对bitset容器进行二进制操作,如^,|,~,<<,>>等等
Access bit (public member function) //可以用如数组形式的赋值。bitset<4> b; b[0]=1;
Set bits (public member function)//默认将容器中所有值赋为1,也可以将特定的位置赋给特定的值
如 bitset<4> b; b.set(); //1111. b.set(2,0) // 1011.
Reset bits (public member function) //默认将容器中所有值赋值为0,也可以将特定位置赋特定的值
Flip bits (public member function)//默认将容器中的数取反,1变0,0变1,也可以将特定位置取反bitset<4>b(string ("0001")); b.file(2); // 0101; b.file(); //1010
Convert to unsigned longinteger (public member function) //将容器的值转化成10进制的数
Convert to string (public member function) //将容器累的值转为字符串
Count bits set (public member function) //统计容器中1的个数
Return size (public member function) //容器的大小
Return bit value (public member function) //返回每个位置上的数
Test if any bit is set (public member function) //容器的值>0返回真,反之。
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=new T[capacity = INIT]; //创建一个T类型的数组
}
~Array() { delete [] data; }
void push_back(const T& t)
{
if(count == capacity) //如果数量和总的个数一样,说明内存不够
{
size_t newCap=2*capacity; //把内存空间增加一倍
T* newData=new T[newCap];
//把原来的数据移动到新的数组上
for(int i=0 ; i<count ; ++i)
newData[i]=data[i];
//回收旧的空间
delete []data;
//更新数据成员
data=newData;
capacity=newCap;
}
//如果还有空余空间
data[count++]=t; //把t赋值给最后的一个空闲的地方
}
void pop_back()
{
if(count > 0)
--count; //把最后一位去掉
}
T* begin() { return data; }
T* end() { return data+count; }
};
template<class T, template<class> class Seq> //这里可以这样写template<class T, template<class U> class Seq>
class Container
{
Seq<T> seq; //参照上面那个模板,这里seq就相当于T
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; //template<class T> class Array 这里seq就相当于Array
container.append(1);
container.append(2);
int* p=container.begin();
while(p != container.end())
cout<<*p++<<endl;
return 0;
}
关于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
类型、枚举类型或者变量等。
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;
}
这个程序有一个缺点,就是不能进行隐式转换,因为模板没有提供这些转换。