c++模板技术:模板的类型转换与类型参数推断

一、类型转换与模板类型参数

模板与函数调用一样,使用传递给模板的实参来初始化模板的形参,只是这个参数是模板参数,且通常是类型参数,在模板编程中,需要有一个很重要的思维转换:非模板编程中,处理的通常是变量,变量的类型是变量的属性,而在模板编程中,处理的更多的是类型本身,我们可以将类型本身作为参数来传递,可以根据实参的类型,推断类型参数的值(注意这个值指的就是‘类型’),比如

template <typename T>  void fun(const T& param) ...

我们处理的更多的是类型参数T,而非param

由于模板的特殊性,通常不会对参数进行转换,而是直接生成一个新的模板,但是也是有例外的,模板类型转换原则有以下几条:

1) 顶层const,不论在实参还是形参中,都会被忽略,即对于模板

template <typename T>
void func(T i, T ii) {
    intTraits<T>::isInt();
    intTraits<typename add_const<T>::type>::isInt();
    
}

int i = 0;
const  int ci = 1;

不管是func(i, ci) 还是func(ci, ci)推断出来的T都是int,而不是const int。  

即类型实参传入模板时,实参以及形参的顶层const都直接去掉忽略不计,只不过形参的顶层const在模板里边还需要控制形参不能被改变,所以仅仅是在参数传入时被忽略。

2)const转换,可以将一个非const对象的引用(或指针)传递给一个const的引用(或指针)形参,比如下面的代码就不会报错,并且三次调用,T都被推断成string类型那个

template<typename T>
void func(const T&, const T&) {
}

int main() {
    string x= "x";
    const string cx = "ss";     
    func(x, cx);
    func(cx, cx);
    func(x, x);
    return 0;
}

3)数组或函数指针转换:如果函数形参不是引用类型,则可以对数组或函数类型实参进行正常的指针转换。但是如果是引用类型,则不会转换为指针

template <typename T>
void func1( T&,  T&) {
}

template <typename T>
void func2(T, T) {
}
int main() {
    int a[10] = {0}, b[7] = {0};
    func1(a, b);      //a,b类型不一致,出错
    func2(a, b);     // a,b都被转换为指针
    return 0;
}

 

 二、类型参数推断


1, 使用引用保存const属性。

template <typename T>  void f(T& t);

上面的代码中,由于t被加上了引用,const 变成底层const,即:

const int ci;

int i;

其中ci会把T的类型推断为const int,而i会把T的类型推断为int。这样就保留了参数的顶层const属性。

 

2,使用右值绑定与引用折叠保留引用属性。

  按照惯例,f(T &t)可以保留对象的const属性,但是只能接受左值作为实参,而f(const T&)可以接受左值、右值(包括临时量和字面值常量),但是由于T前面的const修饰符,无法保留实参的const属性,即将const int i,const int &ri = i, cosnt int && rvi = std::move(i)作为参数传入f(const T&)中,T的类型都会被推断成int。而不是const int,甚至不是int&,但是注意T为int时,f就是f(int &t),因此依旧是引用,会改变实参的值,即f(const T&t)的写法可以去掉T的const、引用属性,并且保证函数参数以引用方式传递。

  通常不能把右值引用绑定到左值上(但是可以将左值引用绑定到右值上,比如可以只有拷贝构造函数,没有移动构造函数)但是有两处例外:

  1) 如果类型是模板的类型参数,并且它是右值引用,如f(T&&t),那么我们将左值传入函数时,编译器会推断T的类型为左值引用

  2) 由于第一条会将T的类型推断为左值引用,如int&,那么f(T&&t)就变成了f(int &&&),此时编译器会进行折叠,1-3个&会折叠成一个&, 4个&会折叠成&&

template <typename T>  void f(T&& t);
int i = 0;
int ci = 0;
f(3);        //合法,3是右值,T为int,f实例化为void f(int&& t)
f(i);        //合法,i是左值,T为int&,f实例化为void f(int& && t),通过折叠变成f(int &t)
f(ci);       //合法,i是左值,T为const int&,同样的折叠后变成f(const  int&)

右值引用可以在传递右值是将T推断为不带任何引用的类型,同时保证函数形参为右值引用,在传递左值时将T推断为T&,并且保证函数形参为左值引用,并且不论传递左值还是右值,都会讲实参的const属性保留到T中。

 

通过这两条规则我们可以精准的控制模板的类型

 

posted on 2015-09-04 12:02  远近闻名的学渣  阅读(2984)  评论(0)    收藏  举报

导航