第一章:函数模板

文章详细介绍了C++中的模板概念,包括typename的使用、模板实例化过程、多模板参数的处理、函数模板重载以及编程实践中的值传递建议。
摘要由CSDN通过智能技术生成

模板的定义与使用

比较简单,使用typename申明(与class关键字等价)

#include <iostream>

template<typename T>
T T_max(const T& a, const T& b) {
    return b < a ? a : b;
}


int main() {
    int a = 0, b = 2;
    auto c = T_max<int>(a,b);
    std::cout<<c<<std::endl;

    double aa = 0.1,  bb = 0.2;
    auto cc = T_max<double>(aa,bb);
    std::cout<<cc<<std::endl;

    std::string sa = "mathematics";
    std::string sb = "math";
    auto sc = T_max<std::string>(sa,sb);
    std::cout<<sc<<std::endl;
     
}

在编译期,模板会根据使用到的类型参数,生成一个对应参数的函数实体进行编译,这一过程称为:实例化。

模板的编译过程,会首先检查模板代码本身的语法,然后还会检查实例化的代码的有效性。

多模板参数

在有多个模板参数的条件下,因为调用顺序的不同,可能结果也会不同:

#include <iostream>

template<typename T1, typename T2>
T2 func(const T1& a, const T2& b) {
    return a + b;
}

int main() {
    int a = 1;
    double b = 2.3;
    auto c = func<int,double>(a,b);
    std::cout<<c<<std::endl;

    auto d = func<double,int>(b,a);
    std::cout<<d<<std::endl;
}

一种解决该问题的办法,就是额外定义一个模板参数:

#include <iostream>

template<typename T1, typename T2, typename RT>
RT func(const T1& a, const T2& b) {
    return a + b;
}

int main() {
    int a = 1;
    double b = 2.3;
    auto c = func<int,double,double>(a,b);
    std::cout<<c<<std::endl;     
}

不过这样,就不能使用C++17中的模板参数自动推导了,必须显示地指定模板参数。

或者,将不能推导的参数,写在前面:

#include <iostream>

template<typename RT,typename T1, typename T2>
RT func(const T1& a, const T2& b) {
    return a + b;
}

int main() {
    int a = 1;
    double b = 2.3;
    auto c = func<double>(a,b);
    std::cout<<c<<std::endl;     
}

C++14 中,可以不显式地声明返回值类型,由编译器自行判断:

#include <iostream>

template<typename T1, typename T2>
auto func(const T1& a, const T2& b) {
    return a + b;
}

int main() {
    int a = 1;
    double b = 2.3;
    auto c = func(a,b);
    std::cout<<c<<std::endl;     
}

如果是C++11,也想使用auto来自行推断,可以使用decltype来捕获函数类型:

#include <iostream>

template<typename T1, typename T2>
auto func(T1 a, T2 b) -> decltype(true?a:b) {
    return a + b;
}

int main() {
    int a = 1;
    double b = 2.3;
    auto c = func(a,b);
    std::cout<<c<<std::endl;     
}

decltype(true?a:b)的返回值会是一个确定的通用的类型,比如这里,会返回一个int&, c的值类别为int&。

但是这种方式可能会导致悬垂引用问题,所以,需要使用std::decay消除引用:


class A {
public:
    int data;
    explicit A(int data):data(data){}
    void add() { ++data; }
    // A(const A& lhs) = delete;
    // A& operator=(const A& lhs) = delete;
};

bool operator< (const A& lhs, const A& rhs) {
    return lhs.data < rhs.data;
}

// template<typename T1, typename T2>
// auto func(T1 a, T2 b) -> decltype(true?a:b){
//     return b < a ? a : b;
// }


template<typename T1, typename T2>
auto func(T1 a, T2 b) -> typename std::decay<decltype(true?a:b)>::type {
    return b < a ? a : b;
}

int main() {

    A a{1};
    A& aa = a;
    A b{5};
    
    using T = decltype(func(aa,b));

    if (std::is_same<T, A&>::value) {
        std::cout<<"T is A&"<<std::endl;
    } else if(std::is_same<T, A>::value) {
        std::cout<<"T is A"<<std::endl;
    }
    else if(std::is_same<T, A&&>::value) {
        std::cout<<"T is A&&"<<std::endl;
    }
    else {
        std::cout<<"T is other"<<std::endl;
    }

}

或者使用C++11提供的 typename std::common_type<T1,T2>::type 作为返回值:

template<typename T1, typename T2>
typename std::common_type<T1,T2>::type func(T1 a, T2 b) {
    return b < a ? a : b;
}

// C++14 提供了更简便的写法
template<typename T1, typename T2>
std::common_type_t<T1,T2> func(T1 a, T2 b) {
    return b < a ? a : b;
}

// 或者直接定义一个返回值类型
// std::common_type<T1,T2> 也可以在这里被使用
template<typename T1, typename T2, 
typename RT = std::decay_t<decltype(true?T1():T2())> >
RT func(T1 a, T2 b) {
    return b < a ? a : b;
}

函数模板重载

template<typename T> T func (T a, T b) {
    return b < a ? a : b; 
}

// 优先调用非模板函数
int func(int a, int b) {
    std::cout<<"int func()\n";
    return b < a ? a : b;
}

int main() {

    int a = 10;
    int b = 11;
    
    using T = decltype(func(a,b));
    auto c = func(a,b); 
    std::cout<<c<<std::endl;

    auto d = func(1.1,2.2); 
    std::cout<<d<<std::endl;

    // 只有非模板函数才会允许强制转换,如果没有int重载,这里会直接编译不过
    auto e = func(1.1,'a');  
    std::cout<<e<<std::endl;

}

常见问题

  1. 尽量使用值传递,以避免很多复杂问题
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值