Loading... # 一、理解函数模板类型推断 myfunc中T的类型不仅仅取决于实参100, 还取决于tmprv的类型(const T&)有关。 函数模板的形参(tmprv)是如下类型时编译器推断出的 类型模板参数的类型(T),以及最终的函数形参类型(tmprv) ## 1、引用或指针类型 * 若实参是引用类型,那么引用部分会被忽略,T不会被推导为引用类型,这个需要记一下。 * 当向引用类型的形参tmprv传入const类型实参时了,那么形参tmprv会成为const引用(原来是个引用)。 ``` 这里可以看到,实参的const属性会成为类型模板参数T类型推导的组成部分,所以不用担心在myfunc中能够修改原来const属性的实参。 ``` case1: 若实参是引用类型,那么引用部分会被忽略,T不会被推导为引用类型。 case2: T中的const没有了,因为函数模板的形参tmprv里出现const。但不管怎么说,只要实参带const,形参tmprv中终究还是会带着const。 ```cpp template <typename T> void myfunc1(T& tmprv) { cout << "-------------------------myfunc1--T&-------begin----------------" << endl; using boost::typeindex::type_id_with_cvr; cout << "T=" << type_id_with_cvr<T>().pretty_name() << endl; //显示T的类型 cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型 cout << "-----------------------myfunc1----T&-----end------------------" << endl; } template <typename T> void myfunc11(const T& tmprv) { cout << "----------------------const---myfunc1--T&-------begin----------------" << endl; using boost::typeindex::type_id_with_cvr; cout << "T=" << type_id_with_cvr<T>().pretty_name() << endl; //显示T的类型 cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型 cout << "-----------------const------myfunc1----T&-----end------------------" << endl; } template <typename T> void myfunc2(T* tmprv) { cout << "---------------myfunc2--------T*---------begin----------------" << endl; using boost::typeindex::type_id_with_cvr; cout << "T=" << type_id_with_cvr<T>().pretty_name() << endl; //显示T的类型 cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型 cout << "-------------------myfunc2-----T*--------end------------------" << endl; } template <typename T> void myfunc22(const T* tmprv) { cout << "-------------myfunc22--------const T*--------begin----------------" << endl; using boost::typeindex::type_id_with_cvr; cout << "T=" << type_id_with_cvr<T>().pretty_name() << endl; //显示T的类型 cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型 cout << "--------myfunc22--------const T*-----------end------------------" << endl; } //万能引用类型的话就能保留以前的类型 template <typename T> void myfunc3(T&& tmprv) { cout << "____-----begin------____" << endl; using boost::typeindex::type_id_with_cvr; cout << "T=" << type_id_with_cvr<T>().pretty_name() << endl; //显示T的类型 cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型 cout << "____-----end-------____" << endl; } int main() { { //case1:当为T&类型时 **那么当传入引用类型时 T被推导的结果并不会带&** int i = 18; //i的类型是int const int j = i; //j的类型是const int const int& k = i; //k的类型是const int & myfunc1(i); //T = int ,tmprv = int & myfunc1(j); //T = int const ,tmprv = int const & 当向引用类型的形参tmprv传入const类型实参时了,那么形参tmprv会成为const引用 myfunc1(k); //T = int const ,tmprv = int const & } { //case2: 当为const T&类型时 那么当传入引用类型时 int i = 18; //i的类型是int const int j = i; //j的类型是const int const int& k = i; //k的类型是const int & myfunc11(i); //T = int ,tmprv = const int & myfunc11(j); //T = int ,tmprv = const int & T中的const没有了,因为函数模板的形参tmprv里出现const 所以即便void myfunc1(T& tmprv) 中是引用类型也无法修改tmprv的值 myfunc11(k); //T = int ,tmprv = const int & //myfunc(100);//T = int ,tmprv = int && } { //case3: 当为T*类型时 那么当传入指针类型时 T被推导的结果并不会带* int i = 18; const int* pi = &i; myfunc2(&i); //实际结果:T = int tmprv = int * myfunc2(pi); //实际结果:T = int const tmprv = int const * } { //case4: 当为 const T*类型时 那么当传入指针类型时 T被推导的结果并不会带* int i = 18; const int* pi = &i; myfunc22(&i); //实际结果:T = int tmprv = const int * myfunc22(pi); //实际结果:T = int tmprv = int const * } { //case5: 当为 T&& 类型时 int i = 18; //i的类型是int const int j = i; //j的类型是const int const int& k = i; //k的类型是const int & myfunc3(i); //T= int &, tmprv= int & myfunc3(j); //T= int const &, tmprv= int const & myfunc3(k); //T= int const &, tmprv= int const & myfunc3(100); //T= int , tmprv= int && } return 0; } ``` 1. 形参中引用有两个作用: A.是可以通过对形参的修改来修改实参。 B.是传递引用比传值效率高,所以,一般来说,函数模板中的形参建议优先考虑“T &tmprv”这样的形态,这样的形态就不怕实参中的引用被忽略掉而导致开发者想通过对形参的修改达到修改实参的本意无法达成。 1. 如何既想享受形参为引用带来的效率上的提高,又不希望通过形参来修改实参,则函数模板中的形参建议考虑“const T &tmprv”这样的形态。 ## 2、传值方式 ```cpp template <typename T> void myfunc3(T tmprv) { cout << "-------------------------myfunc3--T-------begin----------------" << endl; using boost::typeindex::type_id_with_cvr; cout << "T=" << type_id_with_cvr<T>().pretty_name() << endl; //显示T的类型 cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型 cout << "-----------------------myfunc3----T-----end------------------" << endl; } template <typename T> void myfunc33(const T tmprv) { cout << "-------------------------myfunc3-const-T-------begin----------------" << endl; using boost::typeindex::type_id_with_cvr; cout << "T=" << type_id_with_cvr<T>().pretty_name() << endl; //显示T的类型 cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型 cout << "-----------------------myfunc3--const--T-----end------------------" << endl; } int main() { { //case5: 当为T类型时 因为是值传递,所以推断出来的都不带const和引用类型 都是int类型 int i = 18; //i的类型是int const int j = i; //j的类型是const int const int& k = i; //k的类型是const int & myfunc3(i); //T = int ,tmprv = int myfunc3(j); //T = int ,tmprv = int myfunc3(k); //T = int ,tmprv = int } { //case6: 当为const T类型时 因为是值传递,所以推断出来的都不带const类型 都是int类型 int i = 18; //i的类型是int const int j = i; //j的类型是const int const int& k = i; //k的类型是const int & myfunc33(i); //T = int ,tmprv = int const myfunc33(j); //T = int ,tmprv = int const myfunc33(k); //T = int ,tmprv = int const } return 0; } ``` 1.**若实参是引用类型,则引用部分会被忽略**,T不会被推导为引用类型。**除非手工指定为引用类型(不建议这样写代码)**。 针对第一种情况怎么解决:但是不建议这么写代码 手动加上<int &> ```cpp int& m = i; myfunc<int &>(m); ``` 2\*\*.若实参是const类型,则const部分会被忽略\*\*,T不会推导为const类型(毕竟产生的是新副本) ## 3、传值方式的引申——std::ref与std::cref 当函数模板定义中使用传值方式时,可以通过std::ref和std::cref来以引用方式传递参数。 ```cpp #include <iostream> #include <initializer_list> #include <functional> #include <boost/type_index.hpp> using namespace std; template <typename T> void myfunc4(T tmprv) { cout << "-------------------------myfunc4--T-------begin----------------" << endl; using boost::typeindex::type_id_with_cvr; cout << "T=" << type_id_with_cvr<T>().pretty_name() << endl; //显示T的类型 cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型 cout << "-----------------------myfunc4----T-----end------------------" << endl; } int main() { //case7: 当为 T值传递的方式时,但是形参想传引用 int m = 180; myfunc4(std::ref(m)); //std::ref和std::cref象对象包装器,编译器通过创建一个 class std::reference_wrapper<T>类型的对象 //T=class std::reference_wrapper<int> ,tmprv=class std::reference_wrapper<int> cout << "m=" << m << endl; return 0; } ``` 使用ref一定要包含头文件 //error: ‘ref’ is not a member of ‘std’ ## 4、函数名做实参 ```cpp template <typename T> void myfunc5(T tmprv) { cout << "-------------------------myfunc5--T-------begin----------------" << endl; using boost::typeindex::type_id_with_cvr; cout << "T=" << type_id_with_cvr<T>().pretty_name() << endl; //显示T的类型 cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型 cout << "-----------------------myfunc5----T-----end------------------" << endl; } template <typename T> void myfunc55(T& tmprv) { cout << "-------------------------myfunc55--T&-------begin----------------" << endl; using boost::typeindex::type_id_with_cvr; cout << "T=" << type_id_with_cvr<T>().pretty_name() << endl; //显示T的类型 cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型 cout << "-----------------------myfunc55----T&-----end------------------" << endl; } int main() { { //case8: 当为 T值传递的方式时, 传入的是函数指针时 myfunc5(testFunc); //T=void (__cdecl*)(void),tmprv=void (__cdecl*)(void) } { //case9: 当为 T& 传递的方式时, 传入的是函数指针时 myfunc55(testFunc); //T=void __cdecl(void),tmprv=void (__cdecl&)(void)——tmprv是一个函数引用类型:void(&)(void) } return 0; } ``` ## 5、数组做实参 ```cpp template <typename T> void myfunc6(T tmprv) { cout << "-------------------------myfunc6--T-------begin----------------" << endl; using boost::typeindex::type_id_with_cvr; cout << "T=" << type_id_with_cvr<T>().pretty_name() << endl; //显示T的类型 cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型 cout << "-----------------------myfunc6----T-----end------------------" << endl; } template <typename T> void myfunc66(T& tmprv) { cout << "-------------------------myfunc66--T&-------begin----------------" << endl; using boost::typeindex::type_id_with_cvr; cout << "T=" << type_id_with_cvr<T>().pretty_name() << endl; //显示T的类型 cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型 cout << "-----------------------myfunc66----T-----end------------------" << endl; } int main() { { //case10: 当为 T值传递的方式时,数组做实参 const char mystr1[] = "I Love China!"; myfunc6(mystr1); //T=char const * , tmprv=char const * //case11: 当为 T& 传递的方式时,数组做实参 const char mystr2[] = "I Love China!"; myfunc66(mystr2); //T=char const [14],tmprv=char const (&)[14]--(&)代表数组的一个引用。 } return 0; } ``` 、 ## 6、初始化列表做实参 C++11 ```cpp template <typename T> void myfunc7(std::initializer_list<T> tmprv) { cout << "-------------------------myfunc7--initializer_list<T>-------begin----------------" << endl; using boost::typeindex::type_id_with_cvr; cout << "T=" << type_id_with_cvr<T>().pretty_name() << endl; //显示T的类型 cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型 cout << "-----------------------myfunc7----initializer_list<T>-----end------------------" << endl; } int main() { //case12: 当为 initializer_list<T> 时,传递初始化列表 myfunc7({1,2,3}); //T=int,tmprv=class std::initializer_list<int> } ``` 函数的参数列表也可以使用初始化列表 Note: 在C++11中,标准模板中容器对初始化列表的支持源自<initializer\_list>这个头文件中initializer\_list类模板的支持。 程序员只要#include <initializer\_list>头文件,并且声明了一个initializer\_list<T>模板类为参数的构造函数,同样可以使得自定义的类使用列表初始化。 最后修改:2025 年 07 月 01 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏