zoukankan      html  css  js  c++  java
  • 【C++ Primer 第16章】2. 模板实参推断

    模板实参推断:对于函数模板,编译器利用调用中的函数实参来确定模板参数,从函数实参来确定模板参数的过程被称为模板实参推断。

     类型转换与模板类型参数

    与往常一样,顶层const无论在形参中还是在是实参中,都被会忽略。

    • const转换:可以将一个非const对象的引用(或指针)传递给const的引用(或指针)形参。

    • 数组或函数指针转换:一个数组实参可以转换为一个指向其首元素的指针。类似的,一个函数实参可以抓转换一个该函数类型的指针。

     1 template <typename T> T fobj(T, T)  //实参被拷贝
     2 template <typename T> T fret(const T&, const T&) //引用
     3 
     4 string s1("a value");
     5 const strng s2("another value");
     6 fobj(s1, s2);    //调用fobj(string, string); const被忽略
     7 fret(s1, s2);    //调用fret(const string&, const string&); const被忽略
     8 
     9 int a[10], b[42];
    10 fobj(a, b);       //调用fobj(int*, int*);
    11 fret(a, b);       //错误

     

    使用相同的模板参数类型的函数形参

    一个模板类型形参可以用作多个函数形参的类型,由于只允许有限的几种类型转换,因此传递给这些形参的参数必须具有相同的类型。

     1 template <typename T>
     2 int compare(const T& v1, const T& v2)
     3 {
     4     if (v1 < v2) return -1;
     5     if(v2 > v1) return 1;
     6     return 0;
     7 }
     8 
     9 long lng;
    10 compare(lng, 1024);  //错误:不能实例化compare(long, int);
    11 
    12 template <typename A, typename B>
    13 int compare(const A& v1, const B& v2)  //实参类型不一样,但必须兼容
    14 {
    15     if (v1 < v2) return -1;
    16     if(v2 > v1) return 1;
    17     return 0;
    18 }

    正常类型转化应用于普通函数实参

    函数模板可以有用普通类型定义的参数,即,不涉及模板类型参数的类型,这种函数实参不进行特殊处理。

    1 template <typename T>
    2 ostream &print(ostream *os, const T &obj)
    3 {
    4     return os << obj;
    5 }
    6 
    7 print(cout, 42);  //使用print(ostream &os, const int &obj);
    8 ofstream f("output");
    9 print(f, 10);  //使用print(ostream &os, const int &obj); 将f转换为ostream&

    函数模板显示实参

    在C++中,若函数模板返回类型需要用户指定,那么在定义函数模板时,模板参数的顺序是很重要的,如下代码:

    template <typename T1, typename T2, typename T3>  //模板一
    T1 sum(T2 a, T3 b)
    {
        return a + b;
    }

    在调用的时候就需要指定T1的类型,如:sum<float>(1,2);于是sum函数的返回类型为float。

    但是有时候由于设计者的糟糕设计,会导致一些问题:

    template <typename T1, typename T2, typename T3> //模板二
    T3 sum(T1 a, T2 b)
    {
        return a + b;
    }

    那么再进行上述调用,则会出现问题:sum<float>(1, 2);现在这个调用里指定T1类型为float,但是实际传进来的是1(int类型),会进行隐式类型转换,将1转换为float;T2的类型也可以根据sum(1,2)调用的第二个实参推断出来,这里是可能会是int。那么T3是什么类型呢?显然这里编译器无法推断T3的类型,需要在调用时指定才能推断:

    sum<int, int, int>(1, 2),这样T3就推断出来是int

    在指定显示模板实参时指定的类型是和模板参数匹配的,顺序是一一对应的,如:

    • 使用sum<int>(1, 2)对上面的第一个模板进行调用,那么T1对应int,T2和T3则通过推断得出。
    • 使用sum<int>(1, 2)对上面的第二个模板进行调用,那么T1对应的类型是int,T2可以根据实际穿进去的参数进行推断,这里2为int那么T2类型就是int,那么编译器就无法知道T3的实际类型了。

    尾置返回类型与类型转换

     函数指针和实参推断

    转发

    某些函数需要将一个或多个实参连同类型不变地转发给其他函数。在次情况下,我们需要保持被转发实参的所有性质,包括实参类型是否是const的以及实参是左值还是右值。

     1 //接受一个可调用对象和另外两个参数的模板
     2 template <typename F, typename T1, typename T2>
     3 void flips(F f, T1 t1, T2 t2)
     4 {
     5     f(t2, t1)
     6 }
     7 
     8 void f(int v1, int &v2)  //注意v2是一个引用
     9 {
    10     cout << v1 << " " << ++t2 << endl;
    11 }
    12 
    13 f(42, i);         //f改变了实参i
    14 flip1(f, j, 42);  //通过flip1调用不会改变j
    15                   // void flip1(void(*fcn)(int, int&), int ti, int t2);

    定义能保持类型信息的函数参数

     1 template <typename F, typename T1, typename T2>
     2 void flips(F f, T1 &&t1, T2 &&t2)
     3 {
     4     f(t2, t1)
     5 }
     6 
     7 void f(int v1, int &v2)  //注意v2是一个引用
     8 {
     9     cout << v1 << " " << ++t2 << endl;
    10 }
    11 
    12 flip1(f, j, 42);  
     1 template <typename F, typename T1, typename T2>
     2 void flips(F f, T1 &&t1, T2 &&t2)
     3 {
     4     f(t2, t1)
     5 }
     6 
     7 void g(int &&v1, int &v2)  
     8 {
     9     cout << v1 << " " << ++t2 << endl;
    10 }
    11 
    12 flip1(g, j, 42);  //错误:不能从一个左值实例化int&&

    在调用中使用std::forward保持类型信息

    • forward定义在头文件utility中,与move不同,forward必须显式模板来调用。

    • foeward返回该类型显式实参类型的右值引用,即,forward<T>的返回类型是T&&。

     1 template <typename F, typename T1, typename T2>
     2 void flips(F f, T1 &&t1, T2 &&t2)
     3 {
     4     f(t2, t1)
     5 }
     6 
     7 void g(int &&v1, int &v2)  
     8 {
     9     cout << v1 << " " << ++t2 << endl;
    10 }
    11 
    12 flip1(f, j, 42);  //错误:不能从一个左值实例化int&&
    13 
    14 修改为:
    15 
    16 template <typename F, typename T1, typename T2>
    17 void flips(F f, T1 &&t1, T2 &&t2)
    18 {
    19     f(std::forward<T2> (t2), std::forward<T1> (t1))
    20 }
    21 
    22 void g(int &&v1, int &v2)  
    23 {
    24     cout << v1 << " " << ++t2 << endl;
    25 }
    26 
    27 flip1(f, j, 42);  //i将以int&类型传递给g,42将以in&&类型传递给g
  • 相关阅读:
    十代雅阁广东车友群,雅阁广州车友群,深圳雅阁车友群,雅阁微信群、雅阁车友群、十代雅阁交流微信QQ群
    雅阁微信群、雅阁车友群、十代雅阁交流微信QQ群
    [MySQL]快速解决"Table '.sjzlfzbp_post' is marked as crashed and should be repaired"故障
    雅阁微信群、雅阁车友群、十代雅阁交流微信QQ群
    node调试工具--nodemon使用简介
    IIS7.5 URL文件名有加号或空格显示404错误的解决办法
    Mysql CPU使用率长期100%的解决思路备忘
    Windows下通过CMD命令行程序操作MySQL数据库
    软技能:十步学习法
    Linux 文件系统的基本结构
  • 原文地址:https://www.cnblogs.com/sunbines/p/9082535.html
Copyright © 2011-2022 走看看