C++学习——模板与泛型(2)

一、普通类的成员函数模板

1、不管普通类还是类模板,它的成员函数可以是一个函数模板,成为”成员函数模板“, 不可以是虚函数,否则编译器报错;

class A
{
public:
    template<typename T>
    void MyFt(T tmpt)//成员函数模板
    {
    	cout << "tmpt : " << tmpt << endl;
    }
};

int main()
{
    A a;
    a.MyFt(3);//函数模板自己会类型推断,自己推断出int型
              //编译器在遇到这条语句的时候,编译器就会实例化这个函数模板
    return 0;
}

二、类模板的成员函数模板

1、类模板的模板参数必须用<>指定,成员函数模板(函数模板)的参数都可以编译器推断

template<typename C>
class A
{
public:
    template<typename T1>
    A(T1 v1, T1 v2);

    template<typename T2>
    void MyFt(T2 tmpt)
    {
    	cout << "tmpt : " << tmpt << endl;
    }
};

template<typename C>
template<typename T1>
A<C>::A(T1 v1, T1 v2)
{
    cout << "有参构造函数执行" << endl;
}

int main()
{
    A<float> a(1, 2);
    A<float> a2(1.1, 1.2);//A<float>已经实例化了,编译器不会在实例化一个A<float>类       
                          //型了
    return 0;
}

2、类模板的成员函数(包括普通成员函数/成员函数模板) 只有为程序所用(代码中出现了对该函数或者该函数模板的调用时)才进行实例化。如果某函数从未使用,则不会实例化该成员函数,在整个代码段或者其他段中都没有编译进去;

三、模板显式实例化、模板声明
1、为了防止在.cpp文件中都实例化相同的类模板,所以c++11提出了解决方法,称为“显示实例化”,通过“显示实例化”来避免这种生成多个相同类模板实力的开销:

2、显示实例化实现:

//x.h
template<typename C>
class A
{
    ...
};

template <typename T>
void myFunc(T i1, T i2)
{
    ...
}

//project1.cpp
//“显式实例化”手段中的“实例化定义”,这种实例化定义只需要在一个.cpp文件写就可以了
template class A<float>;
template void myFunc(int v1, int v2);

//project2.cpp
//其他cpp文件使用A<float>
//“显式实例化”手段中的“实例化声明”
extern template class A<float>;
extern template void myFunc(int v1, int v2);
//extern作用:不会在本文件中生成一个extern后边所表示的模板实例化版本代码。
//extern目的:告诉编译器,在其他的源文件(.cpp文件)中已经有了一个该模板的实例化版本了。

四、using定义模板别名

1、typedef一般用来定义类型别名;

typedef usigned int uint_t;//相当于给unsigned int类型起了一个别名uint_t]
uint_t abc;

2、类模板使用typedef获取别名:

typedef std::map<std::string, int> map_s_i;
map_s_i myMap;
myMap.insert({"first", 1});

3、希望定义一个类型,如:std::map<std::string, 类型自己定>,以下是c++11实现。

template<typename T>
using str_map_t = std::map<std::string, T>;//str_map_t是类型别名
                                           //using用来给一个“类型模板”起名字(别名)用的
int main()
{
    str_map_t<int> map;
    map.insert({"second", 2});
    return 0;
}

      using在用于定义类型或类型模板的时候,其包含了typedef的所有功能,如下;

typedef usigned int uint_t;//typedef定义类型的方法感觉像定义一个变量:类型名 变量名
using uint_t = unsigned int;//using定义类型的定义方法感觉像赋值一样

typedef std::map<std::string, int> map_s_i;
using map_s_i = std::map<std::string, int>;

template<typename T>
using myFunc_M = int(*)(T, T);//定义类型模板,是个函数指针模板

int myTestFunc(int i1, int i2)
{
    cout << "myTestFunc" << endl;
    return 0;
}

int main()
{
    myFunc_M<int> mf = myTestFunc;
    mf(1, 2);
    return 0;
}

4、using中使用这种模板,既不是类模板也不是函数模板,我们可以看成是一种新的模板类型:类型模板(模板别名)

五、显式指定模板参数

template<typename T1, typename T2, typename T3>
T1 sum(T2 i1, T3 i2)
{
    T1 result = i1 + i2;
    return result;
}
int main()
{
    //显示指定模板参数
    auto result = sum<long, long, long>(2000000000, 2000000000);
    cout << result << endl;
    return 0;
}

六、类模板特化

1、泛化:模板,可以随便指定类型

2、特化:对特殊的类型(类型模板参数)进行特殊对待,给它开小灶,给它写合适它的专用代码;

3、类模板全特化:

template<typename T, typename U>
class TC //泛化的TC类模板
{
    void functest()
    {
        cout << "泛化版本" << endl;
    }
};

       1)全特化:就是所有类型模板参数(如下T和U),都得用具体的类型代表;     

template<typename T, typename U>//特化版本必须要有泛化版本
class TC //泛化的TC类模板
{
    void functest()
    {
        cout << "泛化版本" << endl;
    }
};

template<>//全特化:所有类型模板参数都用具体类型代表,所以这里的template后边的<>里就为空
class TC<int, int>//上边的T绑定到这里的第一个int,上边的U绑定到这里的第二个int
{
    //在这里可以对该特化版本做单独处理
    void functest()
    {
        cout << "int, int的特化版本" << endl; 
    }
};
//一个泛化版本,可以有无数个特化版本
template<>
class TC<double, int>
{
    void functest()
    {
        cout << "double, int的特化版本" << endl; 
    }
};

int main()
{
    TC<int, double, double> td;
    td.functest();
    return 0;
}

       2)常规全特化:

            a)注意:必须先有泛化版本,才能存在特化版本。只要涉及特化,一定先存在泛化;
            b)当T和U这两个类型模板参数都为int类型时,我们希望做一个特化版本;
            c)编译器会优先选择特化版本代码;

template<typename T, typename U>//特化版本必须要有泛化版本
class TC //泛化的TC类模板
{
    void functest()
    {
        cout << "泛化版本" << endl;
    }
};

template<>//全特化:所有类型模板参数都用具体类型代表,所以这里的template后边的<>里就为空
class TC<int, int>//上边的T绑定到这里的第一个int,上边的U绑定到这里的第二个int
{
    //在这里可以对该特化版本做单独处理
    void functest()
    {
        cout << "int, int的特化版本" << endl; 
    }
}
//一个泛化版本,可以有无数个特化版本
template<>
class TC<double, int>
{
    void functest()
    {
        cout << "double, int的特化版本" << endl; 
    }
}

int main()
{
    TC<char, int> tcCharInt;
    tcCharInt.functest();//调用泛化版本

    TC<int, int> tcIntInt;
    tcIntInt.functest();//调用int, int的特化版本

    TC<double, int> tcDoubleInt;
    tcDoubleInt.functest();//调用double, int的特化版本
    return 0;
}

       3)特化成员函数,而不是模板:

template<typename T, typename U>//特化版本必须要有泛化版本
class TC //泛化的TC类模板
{
public:
    void functest()
    {
        cout << "泛化版本" << endl;
    }
};

template<>
void TC<double, double>::functest()
{
    cout << "double,double的functest()特化版本" << endl;
}

int main()
{
    TC<double, double> tdd;//泛化版本对象,调用的是泛化版本的构造函数
    tdd.functest();//因为我们特化了double,double类型的functest函数,所以调用的是特化版本的
                   //functest函数
    return 0;
}

4、类模板偏特化(局部特化):
      1)偏特化从两方面说起:
            a)从模板参数数量上:

template<typename T, typename U, typename W>
class TC
{
public:
    void functest()
    {
        cout << "泛化版本" << endl;
    }
};
//从参数数量上进行偏特化,我们现在绑定2个类型模板参数,留一个类型模板参数
template<typename U>//因为另外两个被绑定到具体类型,所以这里只剩下一个U类型模板参数了
class TC<int, U, double>//注意类型模板参数可以跳着来的
{
public:
    void functest()
    {
        cout << "偏特化int, U, double版本" << endl;
    }
};

            b)从模板参数范围上:

template<typename T>//特化版本必须要有泛化版本
class TC //泛化的TC类模板
{
public:
    TC()
    {
    	cout << "泛化版本构造函数" << endl;
    }
    void functest()
    {
        cout << "泛化版本" << endl;
    }
};

template<typename  T>
class TC<const T>
{
public:
    void functest()
    {
    	cout << "const T 特化版本" << endl;
    }
};

template<typename  T>
class TC<T *>
{
public:
    void functest()
    {
    	cout << "T * 特化版本" << endl;
    }
};

template<typename  T>
class TC<T &>
{
public:
    void functest()
    {
        cout << "T & 特化版本" << endl;
    }
};

int main()
{
    TC<int> td;
    td.functest();//泛化版本

    TC<const int> td;
    td.functest();//const T 特化版本

    TC<int *> td1;
    td.functest();//T * 特化版本

    TC<int &> td2;
    td.functest();// T & 特化版本
    return 0;
}

5、偏特化(局部特化)还是模板类型,全特化就不是模板了;

七、函数模板特化

1、函数模板全特化:

template<typename T, typename U>
void tfunc(T &v1, U &v2)//泛化版本
{
    cout << "tfunc泛化版本" << " v1 : " << v1<< " v2 : " << v2 << endl;
}

template<>
void tfunc(int &v1, double &v2)
{
    cout << "tfunc全特化版本" << " v1 : " << v1<< " v2 : " << v2 << endl;
}

int main()
{
//    const char *p = "I Love China!";
//    int i = 12;
//    tfunc(p,i); //执行泛化版本
    int i = 1;
    double di = 2.2;
    tfunc(i, di);//执行全特化版本

    return 0;
}

      a)全特化函数模板实际上等价于实例化一个函数模板,并不等价于一个函数重载;
      b)如果既有特化版本又有重载函数,编译器首先会选择:普通函数>特化版本>泛化版本;

void tfunc<int, double>(int &, double &){};//全特化等价于实例化一个函数模板
void tfunc<int &v1, double &v2>{};//函数重载

2、函数模板偏特化:函数模板不能偏特化;

八、模板特化版本放置位置建议

1、模板定义、实现一般放在.h中;

2、模板的特化版本应该和模板的泛化版本都应该放在同一个.h文件中;

3、泛化版本放在各个特化版本之前;

九、可变参数模板

1、可变参模板(Variadic Template):允许模板中含有0个到任意个模板参数,在语法上也和传统模板不太一样,多了一个“...”;

template<typename ... T>
void functest(T... args)
{
    cout << sizeof...(args) << endl;
}

int main()
{
    functest("abc");//输出1
    functest(1, 2, 3 ,"a");//输出4
    return 0;
}

      1)我们一般把这个args成为“一包”或“一堆”参数,而且这些参数的类型可以各不相同;
      2)我们理解T这种类型的时候,不能把它理解成一个类型,需要理解成零到多个不同的类型,自然对应的参数args也应该是多个不同类型的参数;
      3)这一包参数中可以容纳0到多个模板参数,而且这些模板参数可以为任意的类型;
      4)T后面带了“...”,所以我们称呼T为“可变参类型”。它看起来是一个类型名,实际上它是包含了零或者多个不同的类型(一包类型)
      5)args:可变形参,既然T代表的是一包类型,那显然args代表的就是一包形参;

template<typename T, typename... U>
void myfunc1(const T &firstarg, const U&... otherargs)//(一个参数,一包参数)这种可变参函数模板写法最适合参数包的展开
{
    cout << sizeof...(otherargs) << endl;
}

int main()
{
    myfunc1(1);//输出0
    myfunc1(10, "abc", 12.7);//输出2
    return 0;
}

2、参数包展开:
      1)展开套路比较固定,一般都是用递归函数的方式来展开参数。要求我们在代码编写中,有一个参数包展开函数和一个同名的递归终止函数;

//递归终止函数
void myfunc1()
{
    cout << "参数包展开时执行了递归终止函数myfunc1" << endl;
}

template<typename T, typename... U>
void myfunc1(const T &firstarg, const U&... otherargs)
{
    cout << sizeof...(otherargs) << endl;
    cout << "收到的参数值为:" << firstarg << endl;
    myfunc1(otherargs...);//递归调用,注意写法
}

int main()
{
    myfunc1(10, "abc", 12.7);//输出2
    //1:myfunc1(10, "abc", 12.7);
    	//2:myfunc1("abc", 12.7);
            //3:myfunc1(12.7);
                //4:myfunc1();终止函数
    return 0;
}

十、可变参类模板

1、略!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值