美文网首页
模板函数类型推断

模板函数类型推断

作者: 404Not_Found | 来源:发表于2022-02-10 20:02 被阅读0次
  • 作者: 雪山肥鱼
  • 时间:20220208 23:41
  • 目的: 理解函数模板类型推断
# 查看类型推断结果
# 理解函数模板类型推断
  ## 引用与指针类型
    ### 额外补充(&  类型 丢失)
    ### 形参带const
    ### 编码技巧总结
    ### 形参为指针类型
  ## 万能引用
  ## 值传递
    ### 值传递传入指针
  ## 值传递方式的引申 -- std::ref 于 std::cref
  ## 数组做实参
  ## 函数名做实参
  ## 初始化列表做实参
# auto 类型 常规推断
  ## 传值方式
  ## 指针/引用
  ## 万能引用

查看类型推断结果

通过查看编译器给我们推断的类型结果,来学习c++类型推断的规则

方法:利用 boost 库 查看类型推断
vs2017 添加 boost库 手段:

  1. 右击项目属性,选择包含目录,增加boost路径
$(VC_IncludePath);$(WindowsSDK_IncludePath);D:\tools\boost_1_55_0b1
  • 形参是 const T &
    利用boost 对 形参类型进行
    boost版本:1.68
    推断:
#include <iostream>
#include <vector>
#include <boost/type_index.hpp>
using namespace std;

//如何查看类型推断结果

template <typename T>
void myfunc(const T & tmprv) {
    cout << "--------begin------------" << endl;
    using boost::typeindex::type_id_with_cvr;
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
    cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
    cout << "--------end------------" << endl;
}

int main(int argc, char **argv) {
    //如何查看类型推断结果 -- 编译器给我们的推断结果
    //以来 boost库
    //结果:
    //T = int
    //tmprv = int const &
    //myfunc形参的类型 不仅取决于100, 还取决于tmprv的类型(const T &)相关
    myfunc(100);

    return 0;
}

函数模板的形参取决于:

  1. 100 的类型 即 函数模板推断出的 <T> 类型
  2. 取决于 函数形参 tmprv 的类型 const T&

结果示意:


图片.png
  • 形参是 引用 或者 指针
template <typename T>
void myfunc(T & tmprv) {
    cout << "--------begin------------" << endl;
    using boost::typeindex::type_id_with_cvr;
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
    cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
    cout << "--------end------------" << endl;
}

int main(int argc, char **argv) {
    int i = 10;// i: int
    const int j = i;//j: const int
    const int &k = i;//k: const int &

    /*
    1.  
    */

    
    myfunc(i);// T:int tmprv:int &
    myfunc(j);// T:int const, tmprv:int const &
    myfunc(k);// T: int const, tmprv: int const &
    return 0;
}
图片.png
注意 第 三种情况, T 的 类型是 const int,并非 const int &

结论:

  1. 若实参是引用类型,那么引用部分会被忽略,typename T 并不会推导成 引用类型。依然是 int类型
  2. 当实参传入的是 const int 时, T被判定为 const int, tmprv 被判定为 const int &

额外补充:

根据上一个小节的结论,我们做一个测试.

void mfRef(int & tmprv) {
    tmprv = 12;
}

template <typename T>
void mfRefT(T tmprv) {
    tmprv = 12;
}

int main(int argc, char **argv) {
    int ii = 1;
    int & jj = ii;
    //mfRef(jj);//12
    mfRefT(jj);//1
    cout << ii << endl;
    return 0;
}

非常明显mfRefT 中的T, 虽然实参的类型是 int & ,但 T 仅仅识被判定成 int。所以虽然产出的引用,但是并没有改变ii的值。

如果想修改,非常简单:

template <typename T>
void mfRef(T & tmprv) {

}

形参带const

template <typename T>
void myfunc(const T & tmprv) {
    cout << "--------begin------------" << endl;
    using boost::typeindex::type_id_with_cvr;
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
    cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
    cout << "--------end------------" << endl;
}

int main(int argc, char **argv) {
    int i = 10;// i: int
    const int j = i;//j: const int
    const int &k = i;//k: const int &

    
    myfunc(i);// T:int tmprv:int &
    myfunc(j);// T:int, tmprv:int const &
    myfunc(k);// T: int, tmprv: int const &
    return 0;
}
图片.png

对比没有const:
T 被判定成了 int,竟然去掉了 const属性?

结论:

  1. 如果实际参数是引用,那么typename T 依旧只会被判定为原始类型.
  2. 形参中带 const 时,如果实参是const int or const int &, 则 T 的类型被判定为 int,并非是const int ,此处异于 形参不带const.所以,形参的类型实际上是影响着T的,所以 typename T,也受着形参类型的影响

编码技巧总结

  1. 形参中引用作用
  • 修改实参值
  • 引用比传值效率高
  • 有效考虑用 T & tmprv 处理
  1. 即想享用形参带来的高效,又不希望通过形参来修改原来的值,则形参使用 const T & tmprv 即可。则形参不会被修改。

形参为指针类型

template <typename T>
void myfunc(T* tmprv) {
    cout << "--------begin------------" << endl;
    using boost::typeindex::type_id_with_cvr;
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
    cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
    cout << "--------end------------" << endl;
}

int main(int argc, char **argv) {
    int i = 10;// i: int
    const int * j = &i;//j: const int
    myfunc(&i);//int,int *
    myfunc(j);//int const, int const *

    return 0;
}
图片.png

一切 同 引用相同

当 形参带了 const时:

template <typename T>
void myfunc(const T* tmprv) {
    cout << "--------begin------------" << endl;
    using boost::typeindex::type_id_with_cvr;
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
    cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
    cout << "--------end------------" << endl;
}

int main(int argc, char **argv) {
    int i = 10;// i: int
    const int * j = &i;//j: const int
    myfunc(&i);//int,int *
    myfunc(j);//int, int const *

    return 0;
}
图片.png

差异性和 引用是形参时相同。T 受到 形参加const 的影响,T丢失了 const 属性。为 int。

万能引用

//万能引用
template <typename T>
void myfunc(T &&tmprv) {
    cout << "--------begin------------" << endl;
    using boost::typeindex::type_id_with_cvr;
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
    cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}

int main(int argc, char **argv) {
    int i = 10;// i: int
    const int j = i;//j: const int
    const int &k = i;//k: const int &

    //都是左值,注意这里 T 的类型!
    myfunc(i);//int&, int &
    myfunc(j);//const int &, const int &
    myfunc(k);//const int &, const int & 

    myfunc(100);//int , int &&

    return 0;
}
图片.png

注意形参是 万能引用时,因为传入的是左值,所以 T 这时候并不是单纯的int,而是 int&.

值传递

#if 1
//传值方式
template <typename T>
void myfunc(T tmprv) {
    cout << "--------begin------------" << endl;
    using boost::typeindex::type_id_with_cvr;
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
    cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}

int main(int argc, char **argv) {
    int i = 10;// i: int
    const int j = i;//j: const int
    const int &k = i;//k: const int &

    myfunc(i);//int, int 
    myfunc(j);// int ,  int 
    myfunc(k);// int ,  int 

    return 0;
}
图片.png
  1. 值传递中,T 的类型 引用被忽略,全部传为 T
    除非手动传入 引入
int &m = i
myfunc(m); //& 依旧被忽略
myfunc<int &>(m);//手动指定T 类型
图片.png

最好不要应用值传递

  1. typename T const 属性的丢失,因为毕竟产生的是新副本,原来的属性与我无关

值传递传入指针

    char mystr[] = "I love china";
    const char * const p = mystr;
    myfunc(p);//const char *, const char *
图片.png
  1. 第一个const 被保留, 第二个const 属性丢失

值传递的 std:ref()/std:cref()

按章上一小节,因为传入的是副本,并不会影响实参值。
std::ref() 执行引用传递,函数内部修改形参后,则会影响实参。
std:cref() const 引用

当函数模板定义中使用传值方式,可以通过std::ref和std::cref 来引用方式传参。
template <typename T>
void myfunc(T tmprv) {
    cout << "--------begin------------" << endl;
    using boost::typeindex::type_id_with_cvr;
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
    cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}

int main(int argc, char **argv) {
    int m = 180;
    myfunc(std::ref(m));//std::ref()/std::cref();是对象包装器,
    /*
    std::ref() - 对象的包装器(是一个模板函数,返回下面的一个类)
    编译器动作:创建一个类型为
    class std::reference_wrapper<T> 的对象
    即 myfunc中传递的 就是 上述的一个对象。
    */
    cout << "m = " << m << endl;

    return 0;
}

注意上述注释中说法:


图片.png

如何修改值呢?

//在函数中增加:
int & tmpvaluec = tmprv;
tmpvaluec = 1200;

数组做实参

  • 形参为 T
template <typename T>
void myfunc(T tmprv) {
    cout << "--------begin------------" << endl;
    using boost::typeindex::type_id_with_cvr;
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
    cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}

int main(int argc, char **argv) {
    const char mystr[] = "I love china"; 
    myfunc(mystr); //const char *, const char *
    return 0;
}
图片.png
  • 形参为 T&
template <typename T>
void myfunc(T & tmprv) {
    cout << "--------begin------------" << endl;
    using boost::typeindex::type_id_with_cvr;
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
    cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}

int main(int argc, char **argv) {
    const char mystr[] = "I love china"; 
    myfunc(mystr);//const char [14], const char (&) [14] 
    return 0;
}
图片.png

tmprv 被判定成了 const char (&)[14] 数组的引用。
如果写法如下:

template <typename T>
void myfunc(T(&)[L1]) {
  cout<<"L1"<<L1<<endl;
}

函数名做实参

  • 值传递
template <typename T>
void myfunc(T tmprv) {
    cout << "--------begin------------" << endl;
    using boost::typeindex::type_id_with_cvr;
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
    cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}

void testFunc() {

}

int main(int argc, char **argv) {
    myfunc(testFunc);
    return 0;
}
图片.png

--__cdecl函数调用方式而已。接触到再详细搜索--

T: 函数指针类型
tmprv: 函数指针类型

  • 传递引用方式
template <typename T>
void myfunc(T & tmprv) {
    cout << "--------begin------------" << endl;
    using boost::typeindex::type_id_with_cvr;
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
    cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}

void testFunc() {

}

int main(int argc, char **argv) {
    myfunc(testFunc);
    return 0;
}
图片.png

tmprv: 变成一个函数引用 void(&)(void)

初始化列表做实参

#include <initializer_list>
template <typename T>
void myfunc(std::initializer_list<T> tmprv) {
    cout << "--------begin------------" << endl;
    using boost::typeindex::type_id_with_cvr;
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
    cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}

int main(int argc, char **argv) {
    myfunc({ 1,2,3 });
    return 0;
}

注意tmprv的类型


图片.png

总结

  1. 推断类型,引用类型实参 被判定为 普通实参类型 int
  2. 万能引用类型,实参为左值, 右值 ,推断结果各不相同
  3. 按值传递的实参,传递给形参时,const 不起作用,但是指针则另当别论。
  4. 数组或函数类型,值传递推断中被判定为指针。引用传递时,则铃铛别论。
  5. 当传递的是初始化列表时,则需要名确指定形参类型为 intialized_list类型。

auto 类型常规推断

声明变量的始化根据变量初始化的类型,自动为此变量选择匹配类型。
特点:

  1. auto的自动类型推断发生在编译期间
  2. auto定义变量必须立即初始化,这样才能被推断出来。
  3. 编译期间用真正的类型替换掉auto
  4. 可以和指针 引用 const类型结合使用。
  5. auto推断出来后,会代表一个具体类型.
    将auto 理解成 函数模板中的 typename T, auto类型的变量名,相当于函数模板的形参. 记住这句话就行了.
    实际上还是有两层 auto 相当于T, 那么 x类型 相当于 函数模板形参类型
template <typename t>
void myfunc(std::initializer_list<t> tmprv) {
    cout << "--------begin------------" << endl;
    using boost::typeindex::type_id_with_cvr;
    cout << "t = " << type_id_with_cvr<t>().pretty_name() << endl;
    cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}

int main(int argc, char **argv) {
    auto x = 27;//x = int, auto = int.
    return 0;
}

auto 是一个类型, x也有一个类型.

传值方式

auto 后 直接接变量名.

int main(int argc, char **argv) {
    auto x = 27;//x int, auto int
    const auto x2 = x; //x2 const int, auto :int
    const auto &xy = x;//xy const int, auto : int
    auto xy2 = xy;//xy2:int(复制,不保留原来属性), auto int

    using boost::typeindex::type_id_with_cvr;
    cout << "xy2 = " << type_id_with_cvr<decltype(xy2)>().pretty_name() << endl;

    return 0;
}

总结:

  1. 传值类型会抛弃 const 类型.因为是赋值拷贝

指针或者引用(非万能引用)

auto 后接一个 &, 以及如何验证auto类型

template <typename T>
void tf(const T & tmprv) {//xy 相当于 tmprv, auto 相当于 T
    cout << "--------begin------------" << endl;
    using boost::typeindex::type_id_with_cvr;
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
    cout << "tmprv =" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl;
}

int main(int argc, char **argv) {
    auto x = 27;//x int, auto int
    const auto x2 = x; //x2 const int, auto :int
    const auto &xy = x;//xy const int, auto : int
    auto xy2 = xy;//xy2:int(复制,不保留原来属性), auto int

    tf(xy);

    return 0;
}
图片.png

测试 xy2


图片.png
auto y = new auto(100);//y:int *, auto:int *
const auto * xp = &x;// xp:const int, auto (其实就是T):  int
auto *xp2 = &x; //xp2:int *, auto :int
auto xp3 = &x;//auto: int, xp3 :int* 

总结:

  1. 传指针或者引用方式对auto类型,不会抛弃const限定,但是会抛弃& , 其实就是 typename T 抛弃引用.

万能引用

与函数模板相同

auto && test0 = 222; //auto:int, test0: int&&;
auto && test1 = x; // auto:int, test1:int&

相关文章

  • Effective Modern C++ - 1: 类型推断

    part1 类型推断 item1 模板类型推断: 即 函数模板实参推断 规则非常自然 remember: 模板类型...

  • Chapter 16 Template 模板

    函数模板 往里面传入变量编译器就能推断类型对于函数模板, 可以使用非类型参数(即, 模板中不使用typename,...

  • C/C++中如何获取数组和指针的长度

    获取数组长度 算术表达式 函数模板参数自动推断 标准C++模板库 模板特化与自动类型推断 Visual C++编译...

  • 【C++ Templates(14)】模板实参推断

    推断的过程 对函数调用,推断会比较实参类型和模板参数类型(即T),对要被推断的参数分别推断出替换,每个实参-参数对...

  • STL:

    STL 算法的操作参数可以用函数对象, 也可以用函数指针: (模板)函数实参推断可以推断出操作实参的类型 不用记算...

  • 【Effective Modern C++(1)】类型推断

    01 理解模板类型推断 模板类型推断是auto的基础,但部分特殊情况下模板推断的机制不适用于auto 模板的形式可...

  • 【Effective Modern C++】索引

    本书讲述了C++11/14新特性的用法和原理。1. 类型推断01 理解模板类型推断02 理解auto类型推断03 ...

  • ts函数重载类型推断

    当从函数重载推断类型时,只会从最后一个函数签名中推断,比如: Reference: 当从多个调用签名中推断类型时(...

  • SpringBoot2.0响应式编程系列(二)-函数式编程和la

    函数接口 方法引用 类型推断

  • 函数

    普通定义 为函数定义类型 完整的函数类型 函数类型包含两部分:参数类型返回值类型 推断类型 函数定义时,如果赋值语...

网友评论

      本文标题:模板函数类型推断

      本文链接:https://www.haomeiwen.com/subject/wygikrtx.html