【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=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类型、枚举类型或者变量等。

为了消除歧义,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;
}

这个程序有一个缺点,就是不能进行隐式转换,因为模板没有提供这些转换。

时间: 2024-08-29 22:59:42

【ThinkingInC++】73、深入理解模板的相关文章

《Effective Modern C++》翻译--条款1: 理解模板类型推导

北京2016年1月9日13:47:17 开始第一章的翻译. 第一章名为 类型推断 分为四个条款: 1理解模板类型推导 2理解auto自动类型推导 3理解decltype操作符 4如何对待推导的类型 第一章 类型推导 C++98有一套单一的类型推导的规则用来推导函数模板.C++11轻微的修改了这些规则并且增加了两个推导规则,一个用于auto,一个用于decltype.接着C++14扩展了auto和decltype可以使用的语境.类型推导的普遍应用将程序员从必须拼写那些显然多余的类型中解放了出来,它

现代C++之理解模板类型推断(template type deduction)

理解模板类型推断(template type deduction) 我们往往不能理解一个复杂的系统是如何运作的,但是却知道这个系统能够做什么.C++的模板类型推断便是如此,把参数传递到模板函数往往能让程序员得到满意的结果,但是却不能够比较清晰的描述其中的推断过程.模板类型推断是现代C++中被广泛使用的关键字auto的基础.当在auto上下文中使用模板类型推断的时候,它不会像应用在模板中那么直观,所以理解模板类型推断是如何在auto中运作的就很重要了. 下面将详细讨论.看下面的伪代码: templ

010_模板辅助器方法

相比辅助器方法,模板辅助器方法更智能一些,它们可以指定想要显示的属性,而让MVC框架去判断应该使用什么样的HTML元素.只是,需要一些初期关注才能建立起来,但毕竟是一种显示数据的更为灵活的方式. 这里打算继续使用介绍辅助器方法时使用的项目,但是,CreatePerson.cshtml视图在之前的辅助器方法会在生成的HTML元素上添加data属性,来支持表单验证,这一点在后面对模板辅助器方法的使用时打算禁用,但是,客户端验证特性对程序的其他部分仍然有效,调整后的代码如下(粗体部分为修改的内容):

读书笔记 effective c++ Item 49 理解new-handler的行为

1. new-handler介绍 当操作符new不能满足内存分配请求的时候,它就会抛出异常.很久之前,它会返回一个null指针,一些旧的编译器仍然会这么做.你仍然会看到这种旧行为,但是我会把关于它的讨论推迟到本条款结束的时候. 1.1 调用set_new_handler来指定全局new-handler 在operator new由于不能满足内存分配要求而抛出异常之前,它会调用一个客户指定的叫做new-handler的错误处理函数.(这也不是完全正确的.Operator new的真正行为更加复杂.

18.2.2 简单的类模板

下面用前面的一个例子来说明,为数组定义一个类模板,该数组要对索引值进行边界检查,确保索引值是合法的.尽管标准库提供了数组模板的完整实现方式,但建立自己的数组模板有助于理解模板的工作原理.我们已经很清楚数组的工作原理了,因此下面集中讨论模板的特性.这也更容易使用第20章介绍的标准库中的 Array 模板. 数组模板只有一个类型参数,所以该模板的定义如下: template <typename T> class Array{ //definition of the template.. } Arr

使用模板引擎(Nvelocity)写代码生成器

之前做了一个项目,由于数据库表比较多,每次都手写model,还有数据库操作之类的东西,感觉浪费时间,也没技术含量,还容易出错,最主要的原因还是懒,不想翻来覆去的写这种代码,于是想到要用代码生成器直接生成,在网上找到很多,但是生成的代码要么看着不安逸,毕竟每个人的风格不同,要么就是生成很多自己根本不需要的垃圾代码,正好最近闲来无事,决定自己动手写一个. 一开始我是想到使用stringbuilder拼接字符串来实现,但是字符串拼接起来相当麻烦,代码量也很多,看起乱糟糟的很不舒服,还有就是得注意格式,

thinkphp中模板继承

模板继承是3.1.2版本添加的一项更加灵活的模板布局方式,模板继承不同于模板布局,甚至来说,应该在模板布局的上层.模板继承其实并不难理解,就好比类的继承一样,模板也可以定义一个基础模板(或者是布局),并且其中定义相关的区块(block),然后继承(extend)该基础模板的子模板中就可以对基础模板中定义的区块进行重载.因此,模板继承的优势其实是设计基础模板中的区块(block)和子模板中替换这些区块.每个区块由<block></block>标签组成,并且不支持block标签的嵌套.

(转)浅谈dedecms模板引擎工作原理及自定义标签

理解织梦模板引擎有什么意义?一方面可以更好地自定义标签.更多在于了解织梦系统,理解模板引擎是理解织梦工作原理的第一步.理解织梦会使我们写php代码时更顺手,同时能学习一些php代码的组织方式. 这似乎不是那么简单,如果你只想学习自定义标签,可以看一下“是否需要自定义标签”和““扩展标签””就够了. 一解析式引擎 如果你还没用过dedecms的标签,先用一下,也可以看一下“dedecms网页模板编写”.熟悉一下memberlist这个标签,下面会以这个标签为例. 织梦提供的模板分析引擎有解析式和编

【PHP 模板引擎】Prototype 原型版发布!

在文章的开头,首先要向一直关注我的人说声抱歉!因为原本是打算在前端框架5.0发布之后,就立马完成 PHP 模板引擎的初版.但我没能做到,而且一直拖到了15年元旦才完成,有很严重的拖延症我很惭愧,再次抱歉! 之前有说过以后的作品发布文章都会同步发表相应的 API 使用说明,但我觉得这还不够好而且博客平台对表格的处理和显示不是很友好,导致 API 不能完美的呈现,因此打算只提供 API 链接,大家可以通过链接直接访问到我的官网去查阅手册,那样的阅读体验是最好的.而发布的文章以后则更新一些和 API