C++函数模板

函数模板

  函数模板本身并不是函数,是将本来应该我们做的事情交给编译器去做。

  两种调用方式:(1)自动推导 (2)显示调用

在这里插入图片描述

template<typename T>  //声明一个模板,告诉编译器之后的T为通用类型
T add(T a, T b) {
    return a + b;
}

int main() {
	cout << add(3, 4) << endl;
}

/*
在编译阶段编译器会根据实际情况,通过类模板实例化出:
int add(int a, int b) {
	return a + b;
}
所以说程序调用的是int add(int, int)这个函数,并不是函数模板

可以用命令: 【nm -C 可执行文件】 查看生成的函数信息
例如查看以上代码则会有: 0000000000001250 W int add<int>(int, int)
其中<int>代表这是由类模板生成的函数

注意:
此种情况下传入的参数3,4都是int,在模板推导过程中不存在冲突现象,所以正确生成了函数实例
*/

注意:

  • 普通函数与模板函数共存时,如果普通函数的参数类型可以完全匹配,则执行普通函数,不进行函数模板的实例化,顾名思义就是吃现有的饭,不去考虑还没有做好的饭。
  • 如果普通函数和模板函数共存,普通函数参数类型不完全匹配,而实例化函数可以完全匹配,则进行模板的实例化。
  • 若普通函数和模板函数共存,指定了需要实例化Add<int>(a,b),则进行实例化。
  • 模板必须要确定出T的数据类型,才可以使用
template<typename T>
void func() {
    cout << "func" << endl;
}

int main() {
    func<int>();   //此时函数模板通过参数无法进行类型推导,所以必须显示调用
    return 0;
}

解决推导过程中发生参数冲突的情况

  • 显示调用函数模板
template<typename T>
T add(T a, T b) {
    return a + b;
}

int main() {
    cout << add(3, 4) << endl;            //7
    cout << add(3.3, 4.5) << endl;        //7.8
    //cout << add(3, 4.1) << endl;        //error
    cout << add<int>(3, 4.1) << endl;     //7
    cout << add<double>(3, 4.1) << endl;  //7.1
    return 0;
}
  • 多抽象出一个类型
template<typename T, typename U>
T add(T a, U b) {
    return a + b;
}

int main() {
    cout << add(3, 4) << endl;          //7
    cout << add(3.3, 4.5) << endl;      //7.8
    cout << add(3, 4.5) << endl;        //7    here
    cout << add(4.5, 3) << endl;        //7.5
    return 0;
}

注意:此时出现一个新的问题——无法确定函数模板的返回值类型! 所以要引入decltype来解决

template<typename T, typename U>
decltype(T() + U()) add(T a, U b) {
    return a + b;
}

int main() {
    cout << add(3, 4) << endl;          //7
    cout << add(3.3, 4.5) << endl;      //7.8
    cout << add(3, 4.5) << endl;        //7.5  here
    cout << add(4.5, 3) << endl;        //7.5
    return 0;
}

注意:此时如果a、b是自定义数据类型的话,且没有构造函数,那么这段代码还是存在BUG,所以还要引入返回值后置来解决

内建数据类型为了与用户自定义类对象的语法对接,底层也实现了如int()、double()这种构造函数.

int a(2);
cout << a << endl;  //2  OK

  

decltype关键字

  “declare type”的缩写。,在C++中,作为操作符,用于查询表达式的数据类型。decltype在C++11标准制定时引入,主要是为泛型编程而设计,以解决泛型编程中,由于有些类型由模板参数决定,而难以(甚至不可能)表示之的问题。

  注意:decltype只进行表达式类型推导,不进行表达式求值

template<typename T>
void judge(T a) {
    cout << "unknow type" << endl;
}

template<>
void judge(int a) {
    cout << "int type" << endl;
}

template<>
void judge(double a) {
    cout << "double type" << endl;
}

decltype(1 + 2) t1;
decltype(1.0 + 2) t2;
decltype(1 + 2.0) t3;
decltype('a') t4;

int main() {
    judge(t1);   //int type
    judge(t2);	 //double type
    judge(t3);	 //double type
    judge(t4);	 //unknow type
    return 0;
}

  

  

auto和decltype的对比

  C++11新加了两个关键字:auto和decltype。用于在编译期推导出变量或表达式的类型,方便开发者简化代码。

auto

编译期推导变量类型。 例:auto x = 1;

推导规则

  • 必须初始化。

  • 在一行定义多个变量时,不能有二义性。

  • 无法推导出模板参数。

  • 不能定义数组

  • 不能用于函数参数

  • 不保留引用或const属性。即使用const类型或引用类型的对象来初始化auto对象,那auto也仅仅按照其基本类型进行推导。如果希望推导出引用或者const类型对象的话需要明确指出

    const int ci = i, &cr = ci;
    auto b = ci;        //b是一个int (ci的顶层const特性被忽略了)
    auto c = cr;        //c是一个int (cr是ci的别名,ci本身是一个顶层const)
    auto d = &i;       //d是一个int* (整数的地址就是指向整数的指针)
    
    //如果希望推断出的auto类型是一个顶层const,需明确指出
    const auto f = ci;    //ci的推断类型为int,f是const int
    
    //还可以将引用的类型设为auto
    auto &g = ci;    //g是一个整型常量引用,绑定到ci
    auto &h = 42;    //错误:不能为非常量引用绑定字面值
    cosnt auto &j = 42//正确:可以将常量引用绑定字面值
    

什么时候使用auto?

  • 不影响代码可读性为前提。

  • 作用域范围较小的变量尽量使用auto。

  • 不关心类型的时候。

  • auto必须初始化,可以强制写代码时赋默认值。

  

decltype

  • 与auto不同,decltype保留引用和const属性

    cosnt int ci = 0, &cj = ci;
    decltype(ci) x = 0;      //x的类型是const int
    decltype(cj) y = x;      //y的类型是const int&,y绑定到变量x
    decltype(cj) z;          //错误,z是一个引用,必须初始化
    
  • 若exp是左值,decltype(exp)是exp类型的左值引用

    int a = 0, b = 0;
    decltype(a + b) c = 0; // c是int,因为(a+b)返回一个右值
    decltype(a += b) d = c;// d是int&,因为(a+=b)返回一个左值
    

      

  

  

返回值后置

  在 C++11 中增加了返回类型后置(trailing-return-type,又称跟踪返回类型) 语法,将 decltypeauto 结合起来完成返回值类型的推导

class A {
public:
    A() = delete;
    A(int x) : x(x) {}
    int x;
};

class B {
public:
    B() = delete;
    B(int x) : x(x) {}
    int x;
};

class C {
public:
    C() = default;
    C(int x) : x(x) {}
    int x;
};

//给decltype推导的表达式提供正确的运算规则
C operator+(const A &a, const B &b) {
    return C(a.x + b.x);
}

C operator+(const B &b, const A &a) {
    return C(a.x + b.x);
}

// 注意
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}

ostream& operator<<(ostream &out, const C &c) {
    out << c.x;
    return out;
}

int main() {
    A a(3);
    B b(4);
    cout << add(a, b) << endl;   //7
    cout << add(b, a) << endl;   //7
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值