(7条消息) 【C/C++基础进阶系列】C/C++ 泛型
【C/C++基础进阶系列】C/C++ 泛型 -- 万能引用、模板类型推断、完美转发
【1】万能引用与右值引用
#include <iostream>
#include <vector>
using namespace std;
// 万能引用与右值引用相关的面试题目
// 1.
// void func(int&& param){...} // 右值引用,因为func不是函数模板而是一个普通函数
// 2.
// template <typename T>
// void func(T && tmpvalue){...} // 是万能引用
// 3.
// template <typename T>
// void func(std::vector<T>&& param) { ... } // 右值引用
//
// 什么情形才是万能引用?
// (a) 函数模板中用作函数参数的类型推断(参数中要涉及到类型推断),T&&
// (b)auto &&tmpvalue = ..... 也是万能引用
// 示例
// int number01 = 10;
// auto &&number11 = number01;// number11是左值,auto=int&, number11=int&, 这里产生引用折叠
// const auto number12 = 234;
// auto &&number13 = number12;// auto=int&, number13=const int&
// auto &&number14 = 234;// 赋值给处置是右值,auto=int, number14=int&&
//
// 其他情况的&&,都是右值引用
namespace _nmsp1
{
// 普通的函数
void func(const int &abc) {}
// 函数模板
template <typename T>
void func_1(const T &abc) {}
// 如果实参传递了一个整型左值给形参,
// tmprv的类型最终会被推断为int &类型
// 如果果实参传递了一个整型右值给形参,
// tmprv的类型最终会被推断为int &&类型
void myfunc_2(int &&tmprv) // 参数tmprv是个右值引用类型
{
cout << tmprv << endl;
return;
}
// T&&是一个万能引用类型
// 既能接左值又能接右值
// 如果实参传递了一个整型左值给形参,tmprv的类型最终会被推断为int &类型;
// 如果果实参传递了一个整型右值给形参,tmprv的类型最终会被推断为int &&类型;
template <typename T>
void myfunc_3(T &&tmprv) // 注意,&&是属于tmprv类型的一部分,不是T类型的一部分(&&和T类型没有关系)
{
tmprv = 12; // 不管tmprv的类型是左值引用还是右值引用,都可以给tmprv赋值,因为tmprv本身是个左值
cout << tmprv << endl;
return;
}
template <typename T>
void func_4(std::vector<T> &¶m)
{
}
template <typename T>
void myfunc_5(const T &&tmprv) // 有const修饰,万能引用资格被剥夺,因为&&,所以只能是个右值引用
{
cout << tmprv << endl;
return;
}
template <typename T>
class mytestc
{
public:
void testfunc(T &&x){}; // 这个不是万能引用,而是右值引用,因为testfunc成员函数本身没有涉及到类型推断
public:
template <typename T2>
void testfunc2(T2 &&x){}; // x类型是万能引用类型
};
} // namespace _nmsp1
// 模板进阶知识
// 万能引用(universal reference / forwarding reference,转发引用)
// 类型区别基本含义
//
// universal reference / 万能引用 / 未定义引用基本认识
// 结论,万能引用是一种类型
// 右值引用(全称,右值引用类型)是用&&符号表示,右值引用绑定到右值上
// 万能引用离不开两种语境
// a) 必须是函数模板
// b) 必须发生了模板类型推断并且函数模板形参如T&&
// 结论,T&&是一个万能引用类型
//
// 万能引用资格的剥夺与辨认
// 剥夺,const 会剥夺一个引用成为万能引用的资格,被打回原型成右值引用
// 辨认
int main()
{
// 调用函数模板
// T = int,abc = const int&
_nmsp1::func_1(10);
// myfunc_2 是右值引用
_nmsp1::myfunc_2(10); // 正确,右值做实参
int i = 100; // i左值
// _nmsp1::myfunc_2(i); // 错误,右值引用不能接(绑)左值
_nmsp1::myfunc_2(std::move(i)); // 只能传递右值进去,必须是std::move(i);
// func_4 是右值引用,不能绑定左值
std::vector<int> aa = {1};
_nmsp1::func_4(std::move(aa));
// 当函数模板发生类型推断时,既能接左值又能接右值
int i = 100;
// 左值被传递,因此tmprv是个左值引用即int&,最终i变成12;
_nmsp1::myfunc_3(i);
printf("i = %d\n", i);
i = 200;
// 右值被传递,因此tmprv是个右值引用即int&&,最终i值变成12;
_nmsp1::myfunc_3(std::move(i));
printf("i = %d\n", i);
// 有const修饰,万能引用资格被剥夺,因为&&,所以只能是个右值引用
int i = 100;
_nmsp1::myfunc_5(std::move(i)); // 不可以,只能传递右值进去,必须是std::move(i);
_nmsp1::mytestc<int> mc;
int i = 100;
// 只有当mytestc类实例化之后,成员函数testfunc才会存在,
// 此时成员函数testfunc的入参类型已经确定
mc.testfunc(std::move(i)); // 左值不能绑定到右值引用上,必须修改为std::move(i);
_nmsp1::mytestc<int> myoc;
int i = 10;
// 调用testfunc2时,类型T2需要推导
myoc.testfunc2(i); // int &
myoc.testfunc2(3); // int &&
return 0;
}
【2】模板类型推断
【2.1】函数模板的类型推断(一)
#include <iostream>
#include <vector>
#include <boost/type_index.hpp>
using namespace std;
// 函数模板
template <typename T>
// 使用boost库打印编译器类型推断结果
void myfunc(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;
}
// 理解函数模板类型推断、查看类型推断结果、auto类型推断
// 如何查看类型推断结果
// 当前要解决的问题:如何查看类型推断结果——指编译器给我们进行类型推断的结果
// 最终目的:通过“查看编译器类型推断结果”的手段来学习并掌握C++类型推断的规则
// 如何查看编译器进行类型推断的结果,依赖Boost库——利用这个库把编译器推断出来的类型信息打印出来
int main()
{
// myfunc中T的类型不仅仅取决于实参100,还取决于tmprv的类型(const T&)有关
// 结果 : T=int,tmprv=const int &
myfunc(100);
// 看一看,函数模板的形参(tmprv)是如下类型时,编译器推断出的类型模板参数的类型(T)以及最终的函数形参类型(tmprv)
// 引用或指针类型
// 根据结果得到一些结论:
// 1. 若实参是引用类型,那么引用部分会被忽略,T不会被推导为引用类型(重要)
// 2. 当向引用类型的形参tmprv传入const类型实参时,那么形参tmprv会成为const引用(原来是个引用)
// 可以看到,实参的const属性会成为类型模板参数T类型推导的组成部分,所以不用担心在myfunc中能够修改原来const属性的实参
int i = 18; // 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 const,tmprv=int const &
myfunc(k); // 实际结果:T=int const,tmprv=int const &
return 0;
}
#include <iostream>
#include <vector>
#include <boost/type_index.hpp>
using namespace std;
// 函数模板
template <typename T>
// 使用boost库打印编译器类型推断结果
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; // 显示T的类型
cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; // 显示tmprv的类型
cout << "--------------------------------end------------------" << endl;
}
// 本范例总结
// a) 形参中引用有两个作用
// 1. 可以通过对形参的修改来修改实参;2. 传递引用比传值效率高;
// 所以,一般来说,函数模板中的形参建议优先考虑“T &tmprv”形态,
// 这样的形态就不怕实参中的引用被忽略掉而导致开发者想通过对形参的修改达到修改实参的本意无法达成;
// b) 若既想享受形参为引用带来的效率上的提高,又不希望通过形参来修改实参,则函数模板中的形参建议考虑“const T &tmprv”形态;
int main()
{
// a) 若实参是引用类型,那么引用部分会被忽略,T不会被推导为引用类型
// b) T中的const没有了,因为函数模板的形参tmprv里出现const
// 只要实参带const,形参tmprv中终究还是会带着const;
int i = 18; // i的类型是int
const int j = i; // j的类型是const int
const int &k = i; // k的类型是const int &
myfunc(i); // 实际结果:T =int,tmprv = int const &
myfunc(j); // 实际结果:T=int,tmprv=int const &
myfunc(k); // 实际结果:T=int,tmprv=int const &
}
#include <iostream>
#include <vector>
#include <boost/type_index.hpp>
using namespace std;
// 函数模板
template <typename T>
// 使用boost库打印编译器类型推断结果
void myfunc(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()
{
int i = 18;
const int *pi = &i;
myfunc(&i); // 实际结果:T = int,tmprv = int *
myfunc(pi); // 实际结果:T = int const,tmprv = int const *
}
#include <iostream>
#include <initializer_list>
#include <boost/type_index.hpp>
using namespace std;
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; //显示T的类型
cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型
cout << "--------------------------------end------------------" << endl;
}
// 理解函数模板类型推断、查看类型推断结果、auto类型推断
// 万能引用类型
int main()
{
int i = 18; // 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 const & ,tmprv = int const &
myfunc(k); // T = int const & ,tmprv = int const &
myfunc(100); // T = int ,tmprv = int &&
return 0;
}
【2.2】函数模板的类型推断(二)
#include <iostream>
#include <initializer_list>
#include <boost/type_index.hpp>
using namespace std;
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; //显示T的类型
cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型
cout << "--------------------------------end------------------" << endl;
// 针对 myfunc(p); 调用
// 可以改变指针指向的内存地址,不能改变指针指向内存中的内容
// tmprv = nullptr;
// *tmprv = 'Y'; // 编译报错:error C3892: “tmprv”: 不能给常量赋值
// 针对 myfunc(std::ref(m)); 调用
// tmprv = 12; // 编译报错class std::reference_wrapper<int>没有赋值运算符重载
// 改变std::ref(m)形式入参的正确方法
// int& tmpvaluec = tmprv;
// tmpvaluec = 1200;
}
void testFunc() {}
// 理解函数模板类型推断、查看类型推断结果、auto类型推断
// 传值方式
int main()
{
// 传值方式
// a) 若实参是引用类型,则引用部分会被忽略,T不会被推导为引用类型,除非手工指定为引用类型(不建议这样写代码)
// b) 若实参是const类型,则const部分会被忽略,T不会推导为const类型(毕竟产生的是新副本)
int i = 18; // 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
myfunc(k); // T = int ,tmprv = int
// 手工指定为引用类型(不建议这样写代码)
int &m = i;
myfunc<int &>(m); // T = int & ,tmprv = int &
// 传值方式 -- 传入const char *const p;
char mystr[] = "I Love China";
const char *const p = mystr; // 第一个const表示p指向的目标中的内容不能通过p改变
// 第二个const表示p指向一个内容后,p不可以再指向其他内容(p不可以指向不同目标)
myfunc(p); // T = char const *,tmprv = char const * ,传递给myfunc后,第二个const没有了,第一个const是保留的
// 这表示进入到myfunc函数模板内部后,tmprv指向的内容不能通过tmprv改变,但是tmprv可以指向其他内存地址
// 也就是说tmprv(p)的常量性被忽略了,而tmprv(p)所指向的内容的常量性会被保留
// 结论记一下,如果传递的是const char *或者const char[],这个const会被保留
// 传值方式的引申 -- std::ref与std::cref
// 当函数模板定义中使用传值方式时,可以通过std::ref和std::cref来以引用方式传递参数
// std::ref,可以修改入参,std::cref,不可以修改入参
int m = 180;
myfunc(std::ref(m)); // std::ref和std::cref象对象包装器,编译器会创建一个class std::reference_wrapper<T>类型的对象
// T=class std::reference_wrapper<int> ,tmprv=class std::reference_wrapper<int>
myfunc(std::cref(m)); // T=class std::reference_wrapper<const int> ,tmprv=class std::reference_wrapper<const int>
cout << "m=" << m << endl;
// 传值方式 -- 数组做实参
const char mystr[] = "I Love China!";
myfunc(mystr); // T=char const * , tmprv=char const *
// 传值方式 -- 函数名做实参
myfunc(testFunc); // T=void (__cdecl*)(void),tmprv=void (__cdecl*)(void)
// // 初始化列表做实参
// myfunc({1, 2, 3}); // 编译报错
}
#include <iostream>
#include <initializer_list>
#include <boost/type_index.hpp>
using namespace std;
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; //显示T的类型
cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型
cout << "--------------------------------end------------------" << endl;
}
void testFunc() {}
int main()
{
// 数组做实参
const char mystr[] = "I Love China!";
myfunc(mystr); // T=char const [14],tmprv=char const (&)[14]--(&)代表数组的一个引用
// 函数名做实参
myfunc(testFunc); // T=void __cdecl(void),tmprv=void (__cdecl&)(void)——tmprv是一个函数引用类型:void(&)(void)
}
#include <iostream>
#include <initializer_list>
#include <boost/type_index.hpp>
using namespace std;
void myfunc(T (&tmprv)[L1])
{
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;
cout << L1 << endl;
}
/**
* 小技巧演示
*/
int main()
{
// 数组做实参
const char mystr[] = "I Love China!";
myfunc(mystr); // T=char const [14],tmprv=char const (&)[14]--(&)代表数组的一个引用
// 函数模板中 cout << L1 << endl; 打印 14
}
#include <iostream>
#include <initializer_list>
#include <boost/type_index.hpp>
using namespace std;
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; //显示T的类型
cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型
cout << "--------------------------------end------------------" << endl;
}
// 初始化列表不能直接推断成initializer_list类型,
// 必须在函数模板的形参中明确指出使用initializer_list类型
int main()
{
// 初始化列表做实参
myfunc({1, 2, 3}); // T=int,tmprv=class std::initializer_list<int>
}
【3】完美转发
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
// 函数模板
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; //显示T的类型
cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型
cout << "--------------------------------end------------------" << endl;
}
namespace _nmsp1
{
void funcLast(int v1, int v2)
{
++v2; // 改变v2的值,让其自增1
cout << v1 + v2 << endl;
}
void funcLast1(int v1, int &v2)
{
++v2; // 改变v2的值,让其自增1
cout << v1 + v2 << endl;
}
void funcLast2(int &&v1, int &v2)
{
cout << v1 << endl;
cout << v2 << endl;
}
// 函数模板(跳板函数),把收到的参数以及这些参数相对应的类型不变的转发给其他函数(完美转发)
template <typename F, typename T1, typename T2>
void funcMiddle_Temp(F f, T1 t1, T2 t2) // f函数指针类型为void(*)(int,int),funcLast是函数类型void(int,int)
{
f(t1, t2);
}
} // namespace _nmsp1
// 完美转发
// 完美转发的概念和步骤演绎
// a) 直接调用, funcLast();
// b) 转发, 通过funcMiddle()间接调用funcLast;
// funcMiddle相当于一个跳板函数,如果有参数,那么参数也需要通过funcMiddle中转传递给funcLast()
// c) 完美转发,const,左值,右值;实参的属性完全不丢失,原原本本的通过funcMiddle转发给funcLast,这种转发就是完美转发
//
// 万能引用,实参的所有信息都会传递到万能引用当中去从而让编译器推导出来函数模板最终的形参类型(引用折叠)
// 完美转发,就是使得程序员可以书写接受任意实参的函数模板(funcMiddle_Temp),并将其转发到目标函数(funcLast2),
// 目标函数会接收到与转发函数(funcMiddle_Temp)所接收的完全相同(当然包括类型相同比如保持参数的左值、右值特性)的参数
// 要实现完美转发,就要用到std::forward
int main()
{
// 直接调用funcLast函数
int i = 50;
_nmsp1::funcLast(41, i); // 92,i = 50,i 值没有改变
// 转发,通过函数模板funcMiddle_Temp间接调用funcLast函数
int j = 70;
_nmsp1::funcMiddle_Temp(_nmsp1::funcLast, 20, j); // 91,j = 70,j 值没有改变
{
// 直接调用funcLast1函数
int i = 50;
_nmsp1::funcLast1(41, i); // 92,执行完i = 51
int j = 70;
_nmsp1::funcMiddle_Temp(_nmsp1::funcLast, 20, j); // 91,执行完本函数,j = 70
// 当前情况下 j 被funcMiddle_Temp推断成了int而不是int&
// void funcMiddle_Temp(void(*f)(int,int &),int t1,int t2){...}
}
{
// funcMiddle_Temp模板参数改为万能引用
int j = 70;
_nmsp1::funcMiddle_Temp(_nmsp1::funcLast1, 20, j); // 91,T1=int,t1 = int &&,T2 = int & ,t2 = int &
// j = 71;
}
{
int j = 70;
_nmsp1::funcLast2(20, j); // 20,70
// _nmsp1::funcMiddle_Temp(_nmsp1::funcLast2, 20, j); // 编译报错
// 20->t1(int &&),但是t1本身是左值
}
return 0;
}
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
// 函数模板
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; //显示T的类型
cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型
cout << "--------------------------------end------------------" << endl;
}
namespace _nmsp1
{
void funcLast(int v1, int v2)
{
++v2; // 改变v2的值,让其自增1
cout << v1 + v2 << endl;
}
void funcLast1(int v1, int &v2)
{
++v2; // 改变v2的值,让其自增1
cout << v1 + v2 << endl;
}
void funcLast2(int &&v1, int &v2)
{
cout << v1 << endl;
cout << v2 << endl;
}
// 函数模板(跳板函数),把收到的参数以及这些参数相对应的类型不变的转发给其他函数(完美转发)
template <typename F, typename T1, typename T2>
void funcMiddle_Temp(F f, T1 &&t1, T2 &&t2) // f:函数指针类型void(*)(int,int)
{
f(
std::forward<T1>(t1),
std::forward<T2>(t2));
}
} // namespace _nmsp1
namespace _nmsp2
{
void printInfo(int &t)
{
cout << "printInfo()参数类型为左值引用" << endl;
}
void printInfo(int &&t)
{
cout << "printInfo()参数类型为右值引用" << endl;
}
void printInfo(const int &t)
{
cout << "printInfo()参数类型为const 左值引用" << endl;
}
template <typename T>
void TestF(T &&t)
{
printInfo(std::forward<T>(t));
}
} // namespace _nmsp2
namespace _nmsp3
{
int getData()
{
return 3;
}
void funcLast3(int v1)
{
cout << "v1=" << v1 << endl;
}
void funcMiddle_Temp2()
{
auto &&result = getData(); // getData返回的是右值,所以auto = int ,result = int &&(右值引用)
//....对result做各种运算
funcLast3(
// decltype用于获取表达式的类型并不进行实际的计算
std::forward<decltype(result)>(result));
}
} // namespace _nmsp3
// 完美转发
// std::forward,C++11中专门为转发而存在的函数,该函数要么返回一个左值,要么返回一个右值
// 万能引用类型才是forward能够发挥作用的重要条件
// 理解
// (a) 实参原来是个左值j,到了形参中还是左值t2;forward能够转化回原来该实参的左值或者右值性,所以,forward之后还是个左值;
// (b) 实参原来是个右值20,到了形参中变成了左值t1;forward能够转化回原来该实参的左值或者右值性,所以,forward之后还是个右值;
// forward这个函数有强制把左值转换成右值的能力;所以,forward这个函数只对原来是个右值这种情况有用;
// forward的能力,保持原始实参的左值性或者右值性;
// 总结,完美转发,比较好的解决了参数转发的问题;
//
// 普通参数的完美转发,auto &&
int main()
{
{
int i = 50;
_nmsp1::funcLast(41, i); // 92,执行完毕 i = 50;
int j = 70;
_nmsp1::funcMiddle_Temp(_nmsp1::funcLast, 20, j); // 91,执行完毕 j = 70
}
{
int i = 50;
_nmsp1::funcLast1(41, i); //直接调用,92,执行完 i = 51;
int j = 70;
_nmsp1::funcMiddle_Temp(_nmsp1::funcLast1, 20, j); // 91,T1 = int, t1 = int &&,T2 = int & , t2 = int &
// j = 71;
}
{
int j = 70;
_nmsp1::funcLast2(20, j); // 20,70
_nmsp1::funcMiddle_Temp(_nmsp1::funcLast2, 20, j);
}
_nmsp2::TestF(1); // printInfo()参数类型为右值引用
// 转发给void printInfo(int &&t)函数
int i = 5;
_nmsp2::TestF(i); // printInfo()参数类型为左值引用
// 转发给void printInfo(int &t)函数
_nmsp2::TestF(std::move(i)); // printInfo()参数类型为右值引用
// std::move能够将左值转换成右值
// 转发给void printInfo(int &&t)函数
const int j = 8;
_nmsp2::TestF(j); // printInfo()参数类型为const左值引用
// j是个const左值
// 转发给void printInfo(const int &t)
_nmsp2::TestF(int(12)); // printInfo()参数类型为右值引用
// int(12)是个临时对象,是个右值
// 转发给void printInfo(int &&t)函数
int &&tempvalue = 16;
_nmsp2::TestF(tempvalue); // printInfo()参数类型为左值引用
// 转发给void printInfo(int &t)函数
_nmsp3::funcLast3(_nmsp3::getData());
_nmsp3::funcMiddle_Temp2();
return 0;
}
参考致谢
本博客为博主学习笔记,同时参考了网上众博主的博文以及相关专业书籍,在此表示感谢,本文若存在不足之处,请批评指正。
【1】C++ 新经典
【2】C++11/14 高级编程 Boost 程序库探秘
赞 (0)