目录
在构造函数模板中使用完美转发范例
class Human { public: //构造函数 Human(const string& tmpname) :m_sname(tmpname) { cout << "Human(const string &tmpname)执行" << endl; } private: string m_sname; };
- 调用
string sname = "ZhangSan"; Human myhuman1(sname); Human myhuman2(string("LiSi")); //"LiSi"是const char[5]类型,而string("LiSi")是string类型。临时对象时右值。
- 输出
- 加入右值引用构造函数
class Human { public: //构造函数 Human(const string& tmpname) :m_sname(tmpname) { cout << "Human(const string &tmpname)执行" << endl; } Human(string&& tmpname) :m_sname(tmpname) //tmpname本身是左值,传给m_sname为左值 { cout << "Human(string&& tmpname)执行" << endl; } private: string m_sname; };
- 调用
string sname = "ZhangSan"; Human myhuman1(sname); Human myhuman2(string("LiSi")); //"LiSi"是const char[5]类型,而string("LiSi")是string类型。临时对象时右值。
- 输出
- 对于临时对象,如果有对应的右值引用的构造函数,优先使用右值引用类型。
案例:将构造初始化改成m_sname(std::move(tmpname)) ,传给m_sname为右值
class Human { public: //构造函数 Human(const string& tmpname) :m_sname(tmpname) { cout << "Human(const string &tmpname)执行" << endl; } Human(string&& tmpname) :m_sname(std::move(tmpname)) { cout << "Human(string&& tmpname)执行" << endl; } private: string m_sname; };
- Human(string&& tmpname) :m_sname(std::move(tmpname))
- move并不具备移动能力,把一个左值转换成一个右值。调用string的移动构造函数,将tmpname的值移动到m_sname,移动后tmpname为空。
使用模板方式 + 完美转发
class Human { public: //构造函数模板 template<typename T> Human(T&& tmpname) : m_sname(std::forward<T>(tmpname)) { cout << "Human(T&& tmpname)执行" << endl; } private: string m_sname; };
- 调用
string sname = "ZhangSan"; Human myhuman1(sname); Human myhuman2(string("LiSi")); //"LiSi"是const char[5]类型,而string("LiSi")是string类型。临时对象时右值。
- 输出
案例:构造函数模板对拷贝构造函数造成影响,匹配混乱
class Human { public: //构造函数模板 template<typename T> Human(T&& tmpname) : m_sname(std::forward<T>(tmpname)) { cout << "Human(T&& tmpname)执行" << endl; } //拷贝构造函数 Human(const Human& th) : m_sname(th.m_sname) { cout << "Human(const Human& th)拷贝构造函数执行" << endl; } private: string m_sname; };
- 调用
string sname = "ZhangSan"; Human myhuman1(sname); Human myhuman3(myhuman1); //实际编译器去调用了构造函数模板,而不是调用了拷贝构造函数。
- 构造函数模板对普通的拷贝构造函数造成影响
案例:增加移动构造函数的情形
class Human { public: //构造函数模板 template<typename T> Human(T&& tmpname) : m_sname(std::forward<T>(tmpname)) { cout << "Human(T&& tmpname)执行" << endl; } //拷贝构造函数 Human(const Human& th) : m_sname(th.m_sname) { cout << "Human(const Human& th)拷贝构造函数执行" << endl; } //移动构造函数 Human(Human&& th) : m_sname(std::move(th.m_sname)) { cout << "Human(Human&& th)移动构造函数执行" << endl; } private: string m_sname; };
- 调用
string sname = "ZhangSan"; Human myhuman1(sname); Human myhuman2(string("LiSi")); Human myhuman4(std::move(myhuman1)); const Human myhuman5(string("WangWu")); //有const修饰,优先匹配拷贝构造函数 Human myhuman6(myhuman5);
- 输出
在可变参数模板中使用完美转发范例
常规的在可变参模板使用完美转发
int funcLast(int v1, int& v2) //目标函数 { ++v2; //改变v2的值,让其自增1 cout << v1 + v2 << endl; return v1 + v2; } template <typename F, typename...T> void funcMiddle_Temp(F f, T&&... t) { return f(std::forward<T>(t)...); }
- 调用
int j = 70; funcMiddle_Temp(_nmsp2::funcLast, 20, j); //91 cout << "j = " << j << endl; //71
将目标函数中的返回值通过转发函数返回给调用者函数
- 用到的技术:auto结合decltype构成返回类型后置语法。
template <typename F, typename...T> //auto funcMiddle_Temp(F f, T&&... t)->decltype( f(std::forward<T>(t)...) ) //后置这种方式可能出现引用丢失的问题,建议使用decltype(auto) decltype(auto) funcMiddle_Temp(F f, T&&... t) { return f(std::forward<T>(t)...); } int funcLast(int v1, int& v2) //目标函数 { ++v2; //改变v2的值,让其自增1 cout << v1 + v2 << endl; return v1 + v2; }
- 调用
int j = 70; int k = funcMiddle_Temp(funcLast, 20, j); cout << "j = " << j << endl; //71 cout << "k = " << k << endl; //91
完美转发失败的情形一例
- 使用NULL或者0作为空指针进行参数传递时导致完美转发失败的情况。
//目标函数 void funcLast4(char* p) { //if (p != NULL) if (p != nullptr) { strncpy(p, "abc",3); } } //转发函数 template <typename F, typename...T> void funcMiddle_Temp(F f, T&&... t) { f(std::forward<T>(t)...); }
- 调用
char* p = new char[100]; memset(p, 0, 100); funcLast4(NULL); //成功 funcMiddle_Temp(funcLast4, NULL); //报错
- 报错
- C++11引入nullptr(空指针)
char* p = new char[100]; memset(p, 0, 100); funcMiddle_Temp(funcLast4, nullptr);
- 使用nullptr可正常调用。
【注】错误处理
- 方法:文件最前面加入
#pragma warning(disable : 4996)