天天看點

C++Primer第五版 第十六章習題答案(41~50)

41:

頭檔案

#ifndef REU_TYPE_H
#define REU_TYPE_H

template <typename T> auto sum(const T&a,const T&b) ->decltype(a+b)//将函數的傳回類型指定為a+b的類型
{
	return a+b;
}
#endif REU_TYPE_H
           

main.cpp

#include <iostream>
#include <vector>
#include <list>
#include <string>
#include "Reu_type.h"
using namespace std;


int main(int argc,char** argv)
{
	int a = 566669;
	int b = 595955;
	cout<<sum(a,b);
	cin.get();
	return 0;
}
           

若将傳回類型去掉,則編譯器報錯:

C++Primer第五版 第十六章習題答案(41~50)

42:知識點1:若我們使用一個函數模版初始化一個函數指針或為一個函數指針指派,編譯器會使用指針的類型來推斷模版的實參

int(*p)(const int&,const int&) = compare;//compare中的T類型會根據指針的類型決定,變為int
           

知識點2:當一個參數為函數模版的執行個體的位址時,程式上下文必須滿足:對每個模版實參都能唯一确定其類型或值

知識點3:當模版參數類型是一個左值引用時(T&),隻能傳遞給它一個左值(一個變量、一個傳回引用類型的表達式),實參可以是const類型,如果實參是const,T将會被推斷為const類型

知識點4:當模版參數類型是const T&時,我們可以傳遞給它任何類型的實參(一個非const或const的對象、一個臨時對象、一個字面常量值),由于參數本身是const,T的推斷類型将不會是const類型,const已經是函數參數類型的一部分,是以const将不會出現在T中

知識點5:當函數參數為右值引用時(T&&),正常的綁定規則是隻能傳遞右值的(比如:4),但是卻又兩個例外允許将左值也綁定到右值引用上

第一個例外:當我們将一個左值傳遞給右值引用時,編譯器會推斷模版參數類型為實參的左值引用類型,即将T推斷為T&,以使用左值引用(一般情況下我們不能定義一個引用的引用,但是通過類型别名或者模版參數類型間接定義是可以的)

第二個例外:隻有四個以上的引用符号會轉化為X&& &&折疊為X&&,其他都會折疊為普通的左值引用X&

知識點6:總結以上:若模版函數參數為右值引用,則可以綁定一個左值,且如果傳入實參是左值的話,編譯器推斷出的模版實參類型将是一個左值的引用,且函數參數将會被執行個體化一個普通的左值引用類型

知識點7:由于對于函數模版參數右值引用可傳入左值和右值,左值會将T綁定為int &,右值綁定為int,是以這樣會對程式造成極大的困難,實際中右值引用常用于兩種情況:模版轉發其實參或者模版被重載

答案:

(a):T 為int &,經折疊,val為int &

(b):T 為const int & ,經折疊,val為const int &

(c):T 為int &&,經折疊,val為int &&

43:表達式會留下左值,是以為int &

44:聲明為T,值傳遞

(a):T 為int

(b):T為 int,const被忽略

(c):T為 int

聲明為const T&

(a):T 為int

(b):T為 int

(c):T為 int&&

45:傳遞42,為右值,T被解析為右值,即int &&,折疊後,仍未int &&,正常

傳遞int 類型變量,為左值,T被解析為int &,折疊後,為int & ,這時将int & 傳遞給vector會出錯,因為該左值沒有初始化

42~45用例:

#include <iostream>
#include <vector>
#include <list>
#include <string>
#include "Reu_type.h"
using namespace std;

template <typename T> void func1(T& a)
{

}
template <typename T> void func2(const T& a)
{

}
template <typename T> void func3(T&& a)
{

}
template <typename T> void g(T&& a)
{
	return vector<T> v;
}
int main(int argc,char** argv)
{
	int i;
	const int ci = 0;

	g(42);
	func1(i);//T為int
	func1(ci);//T為const int
	func1(5);//錯誤,5不是左值

	func2(i);//T為int
	func2(ci);//T為int
	func2(5);//T為int,右值綁定

	func3(i);//T為int&
	func3(ci);//T為const int &
	func3(i = ci);
	func3(5);//T為int,右值綁定

	cin.get();
	return 0;
}
           

46:知識點1:标準庫move函數是使用右值引用模版的一個很好的例子,我們不能将一個右值的引用綁定到一個左值上,但是通過move函數就可以完成該操作

知識點2:這一小節了解即可,知道move可接收左值也可以接收右值。

作用:将elem的元素全部轉到dest的空間中

47:知識點1:轉發:某些函數需要将其一個或者多個實參聯同類型不變的轉發給其他函數,需要保留所有的性質,稱為需要轉發實參的所有性質

知識點2:如果一個函數參數是指向模版類型參數的右值引用(T&&),則它對應的實參的const屬性和右值/左值屬性将得到保持,但此方法不能用于接受右值引用的函數

知識點3:當用于一個指向模版參數類型的右值引用參數時(T&&),forward()會保持實參類型的每個細節,頭檔案為utility,後必須加顯式模版參數

up_down.h

#ifndef UP_DOWN_H
#define UP_DOWN_H

template<typename F, typename IT1, typename IT2>
void flip(F f, IT1&&t1, IT2&&t2)//參數為右值引用,且使用forward傳遞參數,可以保證參數的每個細節不變
{
	f(std::forward<IT2>(t2),std::forward<IT1>(t1))
}
#endif UP_DOWN_H
           

main.cpp

#include <iostream>
#include <vector>
#include <list>
#include <string>
#include "up_down.h"
using namespace std;

void f(int& a, int &&b)
{
	cout<<a<<" "<<++b<<endl;
}
int main(int argc,char** argv)
{
	int i = 5;
	flip(f,i,42);
	cin.get();
	return 0;
}
           

48:知識點1:函數模版可以被另一個模版或者普通非模版函數重載:相同名字函數,具有不同數量或者類型的參數

知識點2:若模版被重載,則函數的比對會發生變化:

1:比對過程中,候選函數包括所有的模版實參推斷成功的模版執行個體

2:可行函數按類型轉換來排序,需要類型轉換的排在不需要轉換的後面

3:若有多個函數提供同樣的比對:若是非模版函數,選擇非模版函數。若沒有非模版函數,則那個模版的特例高,選哪個,否則的話,此調用會産生歧義

知識點3:若多個模版皆為精确比對,正常情況下無法區分,但是更特例化的的模版排在前面,比如const T&和T*p,T *p更加特例化

知識點4:C風格字元串指針和字元串字面常量需要進行轉換,在定義任何函數之前,記得聲明所有重載的函數版本,這樣就防止編譯器執行個體化非我們想要的版本

幾種版本的Debug_rep函數:

#ifndef DEBUG_REP_H
#define DEBUG_REP_H

template<typename T> string Debug_rep(const T&t)
{
	cout<<"const T&:"<<t<<endl;
	return t.str();
}

template<typename T> string Debug_rep(T *t)
{
	cout<<"T *t:"<<t<<endl;
	return t.str();
}

template<typename T> string Debug_rep(T t)
{
	cout<<"T t:"<<t<<endl;
	return t.str();
}

string Debug_rep(const string&t)
{
	cout<<"普通版本函數:"<<t<<endl;
	return t.str();
}

string Debug_rep(char *t)
{
	cout<<"普通版本函數(接受char*):"<<t<<endl;
	return Debug_rep(string(t));
}
#endif DEBUG_REP_H
           

49:

f_g.h

#ifndef DEBUG_REP_H
#define DEBUG_REP_H

// template<typename T> string Debug_rep(const T&t)
// {
// 	cout<<"const T&:"<<t<<endl;
// 	return t.str();
// }
// 
// template<typename T> string Debug_rep(T *t)
// {
// 	cout<<"T *t:"<<t<<endl;
// 	return t.str();
// }
// 
// template<typename T> string Debug_rep(T t)
// {
// 	cout<<"T t:"<<t<<endl;
// 	return t.str();
// }
// 
// string Debug_rep(const string&t)
// {
// 	cout<<"普通版本函數:"<<t<<endl;
// 	return t.str();
// }
// 
// string Debug_rep(char *t)
// {
// 	cout<<"普通版本函數(接受char*):"<<t<<endl;
// 	return Debug_rep(string(t));
// }
// 
// template<typename T> string Debug_rep(const T&t)
// {
// 	cout<<"const T&:"<<t<<endl;
// 	return t.str();
// }

template<typename T> void f(T t)
{
	cout<<"f:T t:"<<t<<endl;
}

template<typename T> void g(T *t)
{
	cout<<"g:T *t:"<<t<<endl;
}

template<typename T> void g(T t)
{
	cout<<"g:T t:"<<t<<endl;
}

template<typename T> void f(const T *t)
{
	cout<<"f:const T*:"<<t<<endl;
}
#endif DEBUG_REP_H
           

main.cpp

#include <iostream>
#include <vector>
#include <list>
#include <string>
#include "Debug_rep.h"
using namespace std;

//所有模版的聲明都需要在函數定義前聲明
template<typename T> void f(T t);
template<typename T> void g(T *t);
template<typename T> void g(T t);
template<typename T> void f(const T*t);

int main(int argc,char** argv)
{
	int i = 42,*p = &i;
	const int ci = 0, *p2 = &ci;

	g(42);//調用g(T t)
	g(p);//調用g(T *t)
	g(ci);//調用g(T t)
	g(p2);//調用g(T *t)
	f(42);//調用f(T t)
	f(p);//調用f(T t)
	f(ci);//調用f(T t)
	f(p2);//調用f(const T *t)
	cin.get();
	return 0;
}
           

繼續閱讀