C++ TGP 模板基础知识--01函数模板

基本范例

  • 模板的定义是以template关键字开头
  • 类型模板参数T前面用typename来修饰,所以遇到typename就知道其后面跟的是一个类型,typename可以用class取代
  • 类型模板参数T代表是一个类型,以及前面的修饰符typename/class都用<>括起来
  • T这个名字可以换成任意其他标识符,对程序没有影响,用T只是一种编程习惯

实例化

编译时用具体的类型代替类型模板参数的过程叫做实例化,通过函数模板实例化之后的函数包含三部分:

  • 模板名
  • 后面跟着一对<>
  • <>中间是一个具体类型

模板参数推断

常规参数推断

通过<>可以只指定一部分模板参数的类型,另一部分模板参数的类型可以通过调用时给的实参来推断

  • auto 代替函数模板返回类
    template <typename T, typename U>
    auto my_sub(T tv1, U tv2)
    {
        return tv1 - tv2;
    }
    
  • decltype可以与auto结合使用来构成返回类型后置语法,这种后置语法也就是使用auto和decltype结合来完成返回类型的推导
    template <typename T, typename U>
    auto my_sub(T tv1, U tv2) -> decltype(tv1 - tv2)
    {
        return tv1 - tv2;
    }
    
各种推断的比较以及空模板参数列表的推断
  • 自动推断
  • 指定类型模板参数
  • 指定空模板参数列表,<>作用就是明确告知调用的时函数模板而不是普通函数
    #include <iostream>
    #include <string>
    #include <vector>
    /*函数模板*/
    template <typename T>
    T my_double(T value)
    {
        return value * 2;
    }
    
    int main(int argc, char** argv)
    {
        auto res1 = my_double(3);  // 自动类型推断,T=int
        auto res2 = my_double<int>(16.9);  // 指定类型模板参数,T=int
        auto res3 = my_double<>(16.9);  // 当即存在函数模板又存在普通函数时,<>指定调用函数模板而不是普通函数
        std::cout << res1 << "  " << res2 << "  " << res3 << std::endl;
    
        return 0;
    }
    

重载

函数(函数模板)名字相同,但是参数数量或参数类型不同。 函数模板和函数可以同时存在,此时可以把函数看成时一种重载,当普通函数和函数模板都比较合适时,编译器会优先选择普通函数。

#include <iostream>
#include <string>
#include <vector>
namespace demo {
/*函数模板*/
template <typename T>
void func(T value)
{
    std::cout << "func(T value) " << typeid(value).name() << " value:" << value << std::endl;
}
/*函数模板*/
template <typename T>
void func(T* value)
{
    std::cout << "func(T* value) " << typeid(value).name() << " value:" << *value << std::endl;
}
/*普通函数*/
void func(int value)
{
    std::cout << "func(int value) " << typeid(value).name() << " value:" << value << std::endl;
}
}  // namespace demo

int main(int argc, char** argv)
{
    demo::func(10);
    demo::func(20.0);
    char a = 'a';
    demo::func(a);
    demo::func(&a);
    std::string str = "string";
    demo::func(str);
    demo::func(&str);
    return 0;
}

特化

全特化
  • 泛化:大众化的、常规的,常规情况下写的函数模板都是泛化的函数模板
  • 特化:代表着从泛化版本抽出来的一组子集
  • 全特化:把泛化版本中的所有模板参数都用具体的类型来代替构成的一个特殊的版本(全特化版本)
    函数调用优先级: 普通函数 > 全特化 > 特化 > 泛化
    void func<char, std::string>(char& t, std::string& u) // 全特化
    void func(char& t, std::string& u) // 重载函数
    
偏特化(局部特化)
  • 模板参数数量上的偏特化:函数模板不支持模板参数数量上的偏特化
  • 模板参数范围上的偏特化: int->const int,类型变小:T->T*,T->T&, T-> T&&
  • 通过重载实现模板参数数量上的偏特化
    #include <iostream>
    #include <string>
    #include <vector>
    
    namespace demo {
    /*函数模板*/
    /* 泛化 */
    template <typename T, typename U>
    void func(T& t, U& u)
    {
        std::cout << "泛化 type t:" << typeid(t).name() << " type u:" << typeid(u).name() << " value:" << t << std::endl;
    }
    /* 全特化 */
    template <>
    void func<char, std::string>(char& t, std::string& u)
    {
        std::cout << "全特化 type t:" << typeid(t).name() << " type u:" << typeid(u).name() << " value:" << t << std::endl;
    }
    /* 偏特化 */
    template <typename T, typename U>
    void func(const T& t, const U& u)
    {
        std::cout << "偏特化 type t:" << typeid(t).name() << " type u:" << typeid(u).name() << " value:" << t << std::endl;
    }
    /* 重载 */
    template <typename U>
    void func(char& t, U& u)
    {
        std::cout << "重载 type t:" << typeid(t).name() << " type u:" << typeid(u).name() << " value:" << t << std::endl;
    }
    }  // namespace demo
    
    int main(int argc, char** argv)
    {
        char        a   = 'a';
        std::string str = "string";
        demo::func(a, str);
    
        const char        b    = 'a';
        const std::string str1 = "string";
        demo::func(b, str1);
    
        return 0;
    }
    

缺失参数

#include <iostream>
#include <string>
#include <vector>

namespace demo {
/* 普通函数 */
int func(int val1, int val2)
{
    return val1 + val2;
}

using FuncType = int (*)(int, int);  // 函数指针

template <typename T, typename F = FuncType>
void myfunc(T val1, T val2, F pfunc = func)
{
    std::cout << pfunc(val1, val2) << std::endl;
}

template <typename T = int, typename U>
void myfunc2(U u)
{
    T t = u;
    std::cout << "type t:" << typeid(t).name() << " type u:" << typeid(u).name() << " value:" << u << std::endl;
}

}  // namespace demo

int main(int argc, char** argv)
{
    demo::myfunc(10, 20);
    demo::myfunc2(10.23);
    return 0;
}

非类型模板参数

基本概念

前面的函数缪版涉及到的模板参数都是类型模板参数,需要用typename/class来修饰。模板参数还可以时非类型模板参数(普通的参数),非类型模板参数一般都是常量(编译器需要在编译时确定非类型模板参数的值)。不是任何类型的参数都可以作为非类型模板参数,一般允许做非类型模板参数类型如下所示:

  • 整型或枚举、指针类型、左值引用类型、auto或者decltype(auto),…
#include <iostream>
#include <string>
#include <vector>

namespace demo {
// T,U:类型模板参数
// val:非类型模板参数
template <typename T, typename U, int val = 100>
auto my_add(T t, U u)
{
    return val + t + u;
}
}  // namespace demo

int main(int argc, char** argv)
{
    auto res1 = demo::my_add<float, double>(10.2f, 20.1f);
    auto res2 = demo::my_add<float, double, 200>(10.2f, 20.1f);

    std::cout << "res1:" << res1 << " res2:" << res2 << std::endl;
    
    return 0;
}
// res1:130.3 res2:230.3
比较奇怪的语法
  • 不管是类型还是非类型模板参数,如果代码中没有使用则参数名可以省略
    namespace demo {
    // template <typename T, int val = 100>
    template <typename, int>  // 代码中未使用T和val,省略
    auto my_add()
    {
        return 100;
    }
    }  // namespace demo
    
    int main(int argc, char** argv)
    {
        auto res1 = demo::my_add<double, 100>();
        std::cout << "res1:" << res1 << std::endl;
        return 0;
    }
    
  • 类型前面可以增加一个typename修饰,明确标识一个类型
    // 2个typename意义不一样
    // typename T:typename 表示后面是一个类型模板参数,可以用class替换
    // typename int val = 100:typename 标识后面修饰的是一种类型
    template <typename T, typename int val = 100>
    auto my_add()
    {
        return 100;
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

血_影

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值