C++泛型编程

目录

1、介绍:

什么是泛型编程?

如何实现泛型编程?

2、实现方法

1、函数模板    

2、类模板

         定义:

         类模板的成员函数:

成员模板:

类模板和友元:

3、C++11可变参数模板

        简介

        包扩展:

理解包扩展:

        转发参数包:


注:这里讲的是C++11版本

1、介绍:

什么是泛型编程?

        泛型编程是指编写与类型无关的通用代码,是代码复用的一种手段

如何实现泛型编程?

        C++通过模板函数和模板类来对其进行支持。

2、实现方法

1、函数模板    

        我们可以定义一个函数模板(function template),而不是为每个类型都定义一个新函数。一个函数模板就是一个公式,可用来生成针对特定类型的函数版本。

        定义方法: 模板定义以关键字template开始,后跟一个模板参数列表(template parameter list),这是一个逗号分隔的一个或多个模板参数(template parameter)的列表,用尖括号<>包围起来。

        例:

template <typename T>
int compare(const T &v1,const T&2)//其中的T类型由参数类型推导得出
{...}//函数体内容

        模板参数列表:作用很像函数的参数列表,函数列表定义了若干特定类型的局部变量,但未指出如何初始化他们。运行时,调用者提供实参来初始化形参。

       模板参数:表示在类或函数定义中用到的类型或值。当使用模板时,我们(隐式或显式地)指定模板实参(template arguement),将其绑定到模板参数上。

        泛型(这里用T指代):当T被用在参数列表时,会由编译器推导得出,当未被推导出时(例如未在参数列表内使用),则需要在使用时显式对其进行定义,可以使用typenameclass来进行声明;

        例:

template<typename T,typename U>
int Fun(const T &v1,const T&2){
    U a;
    std::cout<<a<<std::endl;
    ...
}
int main()
{
    int c=0,d=0;
    Fun<string>(c,d);//在调用时显式定义U的类型
    return 0;
}

2、类模板

         定义:

        类模板的定义与函数模板类似,以关键字template开始,后跟一个模板参数列表(template parameter list),这是一个逗号分隔的一个或多个模板参数(template parameter)的列表,用尖括号<>包围起来。

        例:

template<class T>
class Foo{
    T a;    
    ...
};

        注意:当使用一个类模板时,我们必须提供额外信息——显示模板实参(explicit template arguement)列表,它们被绑定到模板参数。编译器使用这些实参来实例化出特定的类。

         类模板的成员函数:

        与其它任何类相同,我们既可以在类模板内部也可以在其外部为其定义成员函数,且定义在类模板内部的成员函数都被隐式地声明为内联函数。

        类模板的成员函数本身是一个普通函数。但是,类模板的每个实例都有其自己版本的成员函数。因此类模板的成员函数具有和模板相同的模板参数,所以,定义在类模板之外的成员函数就必须以关键字template开始,后接类模板参数列表。

        与往常一样,当我们在类外定义一个成员时,必须说名成员属于哪个类。而且从一个模板生成的类的名字中必须包含其模板实参。当我们定义一个成员函数时,模板实参与模板形参相同。

        

template<class T>
class Base{
public:
    void print();
    ...
}
template<class T>
void Base<T>::print(){...}
int main(){
    ...
}

成员模板:

        一个类(无论是普通类还是类模板)可以包含本身是模板的函数。这种成员被称为成员模板(member template)。成员模板不能是虚函数。

普通(非模板)类的成员模板

class Base{ 
public: 
    template<typename T>
    void print(T a){
    cout<<a.count()<<endl;
    } 
    template<typename Q>
    void Num(Q q); 
}; 
template<typename Q>
Base::Num(Q q){
     cout<<"The number is you know"<<endl; 
}

模板默认实参和类模板

template<typename T> class Base {
    T a;
public:
    template<typename B>
    void Size(B a) {
     cout << a.size() << endl; 
    } 
    template<typename Q>
    void Num(Q q); 
}; 
template<class T> 
template<typename Q> 
void Base<T>::Num(Q q) {
 cout << "The number is you know" <<' ' <<a<< endl; 
}

类模板和友元:

        当一个类包含一个友元声明时,类与友元各自是否是模板是相互无关的。

如果一个类模板包含一个非模板友元,则友元被授权可以访问所有模板实例。

如果友元自身是模板,类可以授权给所有友元模板实例,也可以只授权给特定实例。

模板类的友元分三类:

1,非模板友元。

2,约束模板友元,即友元的类型取决于类被实例化时的类型。

3,非约束模板友元,即友元的所有具体化都是类的每一个具体化的友元。

               在C++标准中,我们可以将模板类型参数声明为友元:

template<class Type>class Bar{ friend Type; }

这样做之后,Foo就是Bar的友元了。

3、C++11可变参数模板

        简介

        一个可变参数模板(variadic template)就是一个接受可变数目参数的模板函数或模板类,可变数目的参数被称为参数包(parameter packet)。存在两种参数包:模板参数包(template parameter packet),表示领个或多个模板参数;函数参数包(function parameter packet),表示零个或多个函数参数。

        我们用一个省略号来指出一个模板参数或函数参数表示一个包。在一个模板参数列表中,class...或typename...指出接下来的参数表示零个或多个给定类型的非类型参数的列表。在函数参数列表中,如果一个参数类型是一个模板参数包,则此参数也是一个函数参数包,例如:

//Args是一个模板参数包;rest是一个函数参数包
//Args表示零个或多个模板类型参数
//rest表示零个或多个函数参数
template<typename T,typename ...Args>
void foo(const T &t,const Args &...rest);

sizeof...运算符        

        当我们需要知道包中有多少个元素时,可以sizeof...运算符。类似于sizeof,sizeof...也返回一个常量表达式,而且不会对其实参求值:

template<typename T...Args>void g(Args...args)
    {cout<<sizeof...(Args)<<endl;//类型参数的数目
    cout<<sizeof...(args)<<endl;//函数参数的数目
    }

        包扩展:

        对于一个参数包,除了获取其大小外,我们能对它做的唯一的事情就是扩展(expand)它。当扩展一个包时,我们还要提供用于每个扩展元素的的模式(pattern)。扩展一个包就是把它分解为构成的元素,对于每个应用模式,获得扩展后的列表。我们通过在模式右边放一个省略号(,,,)来触发扩展操作。

        省略号放在前面时表示这个对象要扩展;在声明时省略号放在名字前表示这是一个扩展。

        例如,我们的print函数包含两个扩展:

template<typename T,typename... Args>
ostream& print(ostream&os,const &T,const Args&... rest)//扩展Args
{
    os<<t<<", ";
    return print(os,rest...)                 //扩展rest
}

        第一个扩展操作扩展模板参数包,为print生成函数参数列表。第二个扩展操作出现在对print的调用中。此模式为print调用生成参数列表。

        对Args的扩展中,编译器将模式const Args应用到模板参数包Args中的每个元素。因此此模式的扩展结果是一个逗号分隔的零个或多个类型的列表,每个类型都是形如const type&。例如:

print(cout,i,s,42);//包中含有两个参数

此调用被实例化为:

print(ostream&,const int&,const string&,const int&);

        第二个扩展发生在对print的(递归)调用中。在此情况下,模式是函数参数包的名字(即rest)。此模式扩展出一个由包中元素组成的,逗号分隔的列表。因此,这个调用等价于:

print(os,s,42);

理解包扩展:

        上面print函数仅仅将包扩展为其构成元素,C++还允许更复杂的扩展模式。例如,我们可以编写第二个可变参数函数,对其每个实参调用debug_rep(一个函数),然后调用print打印结果string:

template<typename...  Args>
ostream &errorMsg(ostream&os,const Args&... rest)
{
    return print(os,debug_rep(rest)...);
}

与之相对,以下代码会报错:

print(os,debug_rep(rest...));

这段代码的问题是我们在debug_rep调用中扩展了rest,它等价于

print(cerr,debug_rep(fcnName,Code.num,otherdata,"otherdata",item));

就是说这样的话就是扩展了函数debug_rep而并不是扩展了print。

扩展中的模式会独立地应用于高中的每个元素。

        转发参数包:

        在C++11标准下,我们可以组合使用可变参数模板与forwad机制来编写函数,实现将其实参不变地传递给其他的函数。

        介绍一下标准库容器的emplace_back成员,它其实就是一个可变参数成员模板,它其实参在容器管理的内存空间中直接构造一个元素。

std::forward<Args>(args)...

这种写法既扩展了模板参数包Args也扩展了函数参数包args,此模式生成如下形式的元素:

std::forward<Ti>(ti);

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值