模板与泛型编程2

时间:2022-05-15 04:20:28

1.模板实参推断

1.1 自动类型转换

const转换:非const对象的引用(指针)传递给一个const的引用(指针)形参。
数组或函数指针转换:若函数实参不是引用类型,则可以对数组或函数类型的实参应用正常的指针转换。

1.2 函数模板显示实参

template <typename T1,typename T2,typename T3>
T1 sum(T2,T3);            //T2,T3可以通过调用实参推断,T1无法推断,需要显示指定

sum<long long>(23,23.5);  //显示指定T1的类型

1.3 尾置返回类型

原:
  当我们不知道返回结果的准确类型,但知道是所处理序列的元素类型。
解:

template <typename It>
auto fcn1(It beg,It end)->decltype(*beg)
{
    return *beg;         //返回引用
}

template <typename It>
auto fcn2(It beg,It end)->typename remove_reference<decltype(*beg)>::type
{
    return *beg;         //返回序列中一个元素的拷贝
}

1.4 函数指针和实参推断(使指针指向一个模板实例)

template <typename T>
int compare(const T&,const T&){/*...*/}

int (*pf)(const int&,const int&) = compare;  //指向compare的int实例版本

当参数是一个函数模板实例的地址时,程序上下文必须满足:对每个模板参数,能唯一确定其类型或值。

void fcn(int(*)(const string&,const string&));
void fcn(int(*)(const int&,const int&));

fcn(compare);         //歧义
fcn(compare<int>);    //显示指定

2.模板实参推断和引用

2.1 模板实参推断:

/*函数参数为左值引用*/
template <typename T> void f1(T&);       //可以传入i(int),ci(const int),右值不可以
template <typename T> void f2(const T&); //三者均可,T都为int

/*函数参数为右值引用*/
template <typename T> void f3(T&&);      //传入i(int&),ci(const int&),右值(int)

2.2 引用折叠

T& &|T& &&|T&& &————>T&
T&& && ---->T&&

若函数实参是指向模板参数的右值引用(T&&),则可传递给它任意类型的实参。
左值=》T&:函数参数通过引用折叠被实例化为(int& &&=》int&

/*std::move*/
template <typename T>
typename remove_reference<T>::type&& move(T&&t)
{
    return static_cast<typename remove_reference<T>::type&&>(t);
}

2.3转发

参数通过一个函数转发给另一函数发生调用。

template <typename F,typename T1,typename T2>
void flip1(F f,T1 t1,T2 t2)
{
    f(t1,t2);          //调用另一个函数
}

void f(int &v1,int v2){/*改变v1*/}

问题:
  当调用flip1(f , j , 42)时,j的值并没有被改变,改变的只是临时的t1。
改进版:

template <typename F,typename T1,typename T2>
void flip2(F f,T1 &&t1,T2 &&t2)
{
    f(t1,t2);          //调用另一个函数
}

void g(int &v1,int &&v2){/*...*/}

此时调用flip1(f , j , 42),j的值会发生改变,但t2变成了左值(函数参数变量均为左值),无法传给右值引用。
最终版:

template <typename F,typename T1,typename T2>
void flip3(F f,T1 &&t1,T2 &&t2)
{
    f(std::forward<T1>(t1),std::forward<T2>(t2));          //调用另一个函数
}

j –> (int&) t1 –> T1(int&) –> std::forward< T1 > –> int&
42 –> t2(int) –> T2(int) –> std::forward< T2 > –> int&&

2.4 重载与模板

  • 当多个重载模板对一个调用提供同样好的匹配时,应选择最特例化的版本。
  • 对于一个调用,如果一个非函数模板与一个函数模板提供同样好的匹配,则选择非模板版本。

2.5 可变参数模板

参数包:可变数目的参数(≥0),用”“表示一个模板参数为一个包。
sizeof…(参数名):返回包中有多少元素。

template<typename T,typename...Args>        //Args为模板参数包
void fcn(const T&,const Args&...rest);      //args为函数参数包

sizeof...(Args)==sizeof(rest);
fcn(str,42,"hi")==>fcn(const string&,const int&,const char[3]&);

包扩展

  • rest…:生成实参列表
  • fcn(rest)…:对rest包中的每个元素进行进行fcn调用(fcn(r1),fcn(r2)…)
  • std::forward< Args >(args)…:扩展为std::forward< T1 >(t1),std::forward< T2 >(t2)…
/*make_share<T>(...)*/
template <typename T,typename...Args>
shared_ptr<T> make_share(Args&&...args)
{
    return shared_ptr<T>(new T(std::forward<Args>(args)...));
}

2.6模板特例化

通用模板的定义对特定类型不合适,如无法对指针进行比较,本质是实例化一个模板。

/*函数模板特例化*/
template <>                        //<>表明我们将为所有模板参数提供实参
int compare(const char* const&p1,const char* const&p2)
{/*...*/}

/*类模板特例化*/
template <>
class A<int>                      //显示指定特例化类型
{
    /*....*/
};

类模板部分特例化

template<class T>
struct remove_reference{typedef T type;}             //普通
template<class T>
struct remove_reference<T&>{typedef T type;}         //左值
template<class T>
struct remove_reference<T&&>{typedef T type;}        //右值

remove_reference<decltype(42)>::type                 //int-->int
remove_reference<decltype(i)>::type                  //int&-->int
remove_reference<decltype(std::move(i))>::type       //int&&-->int

特例化成员

template <typename T>
struct Foo
{
    void bar(){/*...*/}
};
template<>
void Foo<int>::bar(){/*...*/}     //当T为int时,调用特例化的bar