1. 程式人生 > >c++ primer第五版----學習筆記(十六)Ⅱ

c++ primer第五版----學習筆記(十六)Ⅱ

部分習題解答:

16.1: 當呼叫template時,編譯器使用實參的型別來確定繫結到模板引數T的型別,之後編譯器利用推斷出的模板引數來例項化一個特定版本的函式 16.2:

#ifndef  COMPARE_H
#define  COMPARE_H

template <typename T>
int Compare(const T &val1, const T &val2)
{
	if (val1 < val2)
	{
		return 1;
	}
	else
		return 0;
}
#endif // ! COMPARE_H
//test.cpp
#include <iostream>
#include "Compare.h"
using namespace std;

int main(int argc, char **argv)
{
	cout << Compare(5, 6) << endl;
	system("pause");
        return 0;
}

16.3:

Sales_data未定義“<”運算子

16.4:

#ifndef  FIND_H
#define  FIND_H

template <typename U, typename T>
bool Find(const U &it1, const U &it2, const T &val)
{
	auto it3 = it1;   //傳入引數為const,需要建立一個臨時副本
	for (; it3 != it2; ++it3)
	{
		if (*it3 == val)
		{
			return true;
		}
	}
	return false;
}
#endif // ! FIND_H
//test.cpp
#include <iostream>
#include <vector>
#include <list>
#include <string>
#include "Find.h"
using namespace std;

int main(int argc, char **argv)
{
	vector<int>  vec = { 1,2,4,6,7,4,3,0,5 };
	if (Find(vec.begin(), vec.end(), 5))
	{
		cout << "the number has been found!" << endl;
	}
	else
		cout << "not found" << endl;

	list<string> lst = { "ad","sad","beautiful" };
	if (Find(lst.begin(), lst.end(), "sad"))
	{
		cout << "the string has been found!" << endl;
	}
	else
		cout << "not found" << endl;
	system("pause");
        return 0;
}

16.5:

#ifndef  PRINT_H
#define  PRINT_H

template <typename T>
void Print(const T &arr)
{
	for (auto elem : arr)
	{
		cout << elem << endl;
	}
}
#endif // ! PRINT_H
//test.cpp
#include <iostream>
#include <string>
#include "Print.h"
using namespace std;

int main(int argc, char **argv)
{
	string arr[5] = { "ad", "acs","night","dark","eye"};
	Print(arr);
	system("pause");
        return 0;
}

16.6:

begin()是一個模板函式,接受一個容器的引用,返回指向容器第一個元素的迭代器 end()返回容器尾元素的下一個位置的迭代器

#ifndef  BEGIN_END_H
#define  BEGIN_END_H

template <typename T, size_t size>
T* begin(const T (&arr)[size])
{
	return arr;
}

template <typename T, size_t size>
T* end(const T(&arr)[size])
{
	return (arr + size);
}
#endif // ! BEGIN_END_H
//test.cpp
#include <iostream>
#include "Begin_end.h"
using namespace std;

int main(int argc, char **argv)
{
	int arr[3] = { 0,1,2 };
	cout << *(begin(arr)) << endl;
	cout << *(end(arr) - 1) << endl;
	system("pause");
        return 0;
}

16.7:

#ifndef  ARRAY_SIZE_H
#define  ARRAY_SIZE_H

template <typename T, unsigned N>
size_t array_size(const T(&arr)[N])
{
	return N;
}
#endif // ! ARRAY_SIZE_H
//test.cpp
#include <iostream>
#include "Array_size.h"
using namespace std;

int main(int argc, char **argv)
{
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	cout << "Array_size : " << array_size(arr) << endl;
	system("pause");
        return 0;
}

16.8:

因為大多數型別定義了“!=”而未定義“<”運算子

16.9: 函式模板:用來生成特定型別的函式例項   類模板:用來生成類的藍圖 16.10: 使用顯示模板實參重寫該模板,將模板引數T的每個例項替換為給定的模板實參 16.11: 在類作用域內定義不需要新增實參列表 ListItem必須新增實參列表例項化成一個類才是一個型別

16.12:

#ifndef  BLOB_H_BLOBPTR
#define  BLOB_H_BLOBPTR

#include <iostream>
#include <vector>
#include <memory>
#include <initializer_list>
using namespace std;

template <typename> class BlobPtr;
template <typename> class Blob;
template <typename T>
bool operator==(const Blob<T>&, const Blob<T>&);

template <typename T> class Blob {
	//宣告友元
	friend class BlobPtr<T>;
	friend bool operator==<T>(const Blob<T>&, const Blob<T>&);
public:
	typedef typename vector<T>::size_type size_type;
	//建構函式
	Blob() :
		data(make_shared<vector<T>>()) {}
	Blob(initializer_list<T> il) :
		data(make_shared<vector<T>>(il)) {}

	bool empty() const { return data->empty(); }
	size_type size() const { return data->size(); }

	BlobPtr<T> begin() { return BlobPtr<T>(*this, 0); }
	BlobPtr<T> end() { return BlobPtr<T>(*this, data->size()); }

	T& front();
	T& back();
	const T& front() const;
	const T& back() const;

	T& at(size_type);   
	const T& at(size_type) const;     
	void push_back(const T &val) { data->push_back(val); }    
	void push_back(T &&val) { data->push_back(std::move(val)); }    
	void pop_back();     //過載運算子    
	T& operator[]( size_type i );   
	const T& operator[]( size_type i ) const;
private:    //資料成員   
	std::shared_ptr<std::vector<T>> data;    //輔助函式    
	void check( size_type, const std::string& ) const;
}; 

template <typename T>
void Blob<T>::check( size_type n, const std::string &msg ) const
{    
	if( n >= size() )        
		throw std::out_of_range( msg );
} 

template <typename T>
T& Blob<T>::front() 
{   
	check( 0, "front on empty Blob" );   
	return data->front();
} 

template <typename T>
const T& Blob<T>::front() const
{   
	check( 0, "front on empty Blob" );    
	return data->front();
} 

template <typename T>
T& Blob<T>::back() 
{    
	check( 0, "back on empty Blob" );    
	return data->back();
}

template <typename T>
const T& Blob<T>::back() const
{    
	check( 0, "back on empty Blob" );   
	return data->back();
} 

template <typename T>
void Blob<T>::pop_back()
{   
	check( 0, "pop_back on empty Blob" );    
	data->pop_back();
} 

template <typename T>
T& Blob<T>::at( size_type i )
{   
	check( i, "subscript out of range" );   
	return ( *data )[ i ];
} 

template <typename T>
const T& Blob<T>::at( size_type i ) const
{  
	check( i, "subscript out of range" );    
	return ( *data )[ i ];
} 

template <typename T>
T& Blob<T>::operator[]( size_type i )
{   
	check( i, "subscript out of range" );    
	return ( *data )[ i ];
} 

template <typename T>
const T& Blob<T>::operator[]( size_type i ) const
{    
	check( i, "subscript out of range" );    
	return ( *data )[ i ];
} 

//友元運算子定義
template <typename T>
bool operator==( const Blob<T> &lhs, const Blob<T> &rhs )
{    
	if( lhs.size() != rhs.size() )        
		return false;    
	for( size_t i = 0; i != lhs.size(); ++i )       
		if( lhs[i] != rhs[i] )           
			return false;    
	return true;
} 

template <typename T>    
bool operator==( const BlobPtr<T>&, const BlobPtr<T>& ); 
template <typename T> class BlobPtr{   
	friend bool operator==<T>( const BlobPtr<T>&, const BlobPtr<T>& );
public:   
	BlobPtr(): curr( 0 ) { }    
	BlobPtr( Blob<T> &a, std::size_t sz = 0 ): wptr( a.data ), curr( sz ) { }    
	//公共介面   
	T& operator[]( std::size_t i ){       
		auto ret = check( i, "subscript out of range" );      
		return ( *ret )[ i ];   
	}    
	const T& operator[]( std::size_t i ) const{        
		auto ret = check( i, "subscript out of range" );       
		return ( *ret )[ i ];    
	}     
	T& operator*() const
	{        
		auto ret = check( curr, "dereference past end" );       
		return ( *ret )[ curr ];    
	}     
	T* operator->() const
	{       
		return & this->operator*();   
	}     
	BlobPtr& operator++();   
	BlobPtr& operator--();   
	BlobPtr operator++(int);    
	BlobPtr operator--(int);
private:    
	//資料成員    
	std::weak_ptr<std::vector<T>> wptr;    
	std::size_t curr;  
	//輔助函式   
	std::shared_ptr<std::vector<T>> check( std::size_t, const std::string& ) const;
}; 

template <typename T>
bool operator==( const BlobPtr<T> &lhs, const BlobPtr<T> &rhs )
{    
	return lhs.wptr.lock().get() == rhs.wptr.lock().get() && lhs.curr == rhs.curr;
} 

template <typename T>
bool operator!=( const BlobPtr<T> &lhs, const BlobPtr<T> &rhs )
{   
	return !( lhs == rhs );
} 

template <typename T>
std::shared_ptr<std::vector<T>>BlobPtr<T>::check( std::size_t i, const std::string &msg ) const
{   
	auto ret = wptr.lock();   
	if( !ret )        
		throw std::runtime_error( "unbound BlobPtr" );   
	if( i >= ret->size() )       
		throw std::out_of_range( msg );    
	return ret;
} 

template <typename T>
BlobPtr<T>& BlobPtr<T>::operator++()
{   
	check( curr, "increment past end" );  
	++curr;   
	return *this;
} 

template <typename T>
BlobPtr<T>& BlobPtr<T>::operator--()
{    
	--curr;    
	check( curr, "decrement past begin" );   
	return *this;
} 

template <typename T>
BlobPtr<T> BlobPtr<T>::operator++(int)
{   
	auto ret = *this;    ++*this;    
	return ret;
} 

template <typename T>
BlobPtr<T> BlobPtr<T>::operator--(int)
{    
	auto ret = *this;    
	--*this;   
	return ret;
}

#endif // ! BLOB_H_BLOBPTR

16.13: 為每個BlobPtr例項與用相同型別例項化的運算子建立起一對一的友好關係 ==運算子使用友元關係,因為要訪問到BlobPtr的私有成員 !=運算子不需要友元關係,它可以委託operator==完成任務 16.14、16.15:

#ifndef  SCREEN_H
#define  SCREEN_H

#include <iostream>
#include <string>
using namespace std;

template <unsigned H, unsigned W> class Screen;

template <unsigned H, unsigned W>
ostream& operator<<(ostream&, Screen<H, W>&);

template <unsigned H, unsigned W>
istream& operator>>(istream&, Screen<H, W>&);

template <unsigned H, unsigned W> class Screen {
	//友元宣告
	friend ostream& operator<<(ostream&, Screen<H, W>&); 
	friend istream& operator>>(istream&, Screen<H, W>&);
public:
	//類型別名
	typedef string::size_type pos;
	//建構函式
	Screen(char c = ' '):
		contents(H * W, c) {}
	//介面成員
	char get() const { return contents[cursor]; }  //讀取游標處的字元
	char get(pos row, pos col) const {
		auto ret = check(row, col);
		return contents[ret];
	}

	Screen& set(char ch) { contents[cursor] = ch; return *this; }
	Screen& set(pos row, pos col, char ch)
	{
		auto ret = check(row, col);
		contents[cursor] = ch;
		return *this;
	}

	Screen& move(pos row, pos col) { cursor = check(row, col); return *this; }

	//輸出函式的兩種形式,根據物件是否是const呼叫不同的display函式
	Screen& display(ostream &os) {
		do_display(os);
		return *this;
	}
	const Screen& display(ostream &os) const {
		do_display(os);
		return *this;
	}

private:
	//資料成員
	pos cursor = 0;
	string contents;
	//判斷是否越界
	pos check(pos row, pos col) const {
		if (row >= H)
			throw out_of_range("invalid row");
		if (col >= W)
			throw out_of_range("invalid column");
		return row * W + col;
	}
	//顯示函式
	void do_display(ostream &os)
	{
		for (auto i = 0; i < H; ++i)
		{
			for (auto j = 0; j < W; ++j)
			{
				os << contents[i * W + j];
			}
			os << endl;
		}
	}
};

template <unsigned H, unsigned W>
ostream& operator<<(ostream &os, Screen<H, W> &scr)
{
	scr.do_display(os);
	return os;
}

template <unsigned H, unsigned W>
istream& operator>>(istream &is, Screen<H, W> &scr)
{
	string item;
	is >> item;
	scr.contents = item.substr(0, H * W);
	return is;
}
#endif // ! SCREEN_H
//test.cpp
#include <iostream>
#include "Screen.h"
using namespace std;

int main(int argc, char **argv)
{
	Screen<5, 5> myScreen('#');
	myScreen.move(4, 0).set('*');
	cout << myScreen.get(4, 0) << endl;
	myScreen.display(cout);
	system("pause");
    return 0;
}

16.16:

#ifndef  VEC_H
#define  VEC_H

#include <memory>
#include <utility>
#include <initializer_list>
#include <string>

using namespace std;

template <typename T> class Vec {
public:
	//預設建構函式
	Vec() :
		elements(nullptr), first_free(nullptr), cap(nullptr) { }
	//其他建構函式
	Vec(initializer_list<T>&);
	//拷貝建構函式
	Vec(const Vec&);
	//解構函式
	~Vec();
	//拷貝賦值運算子
	Vec& operator=(const Vec&);
	void push_back(const T &);       //拷貝元素
	size_t size() const { return first_free - elements; }   //存有元素的長度
	size_t capacity() const { return cap - elements; }       //分配的記憶體
	void resize(size_t n);                  //
	void reserve(size_t n);
	T* begin() const { return elements; }
	T* end() const { return first_free; }
private:
	//資料成員
	static allocator<T> alloc;      //分配元素
	T *elements;             //指向陣列首元素的指標
	T *first_free;           //指向陣列第一個空閒元素的指標
	T *cap;                  //指向陣列尾後位置的指標
								  //工具函式
	void chk_n_alloc();              //判斷是否有空閒空間容納新元素
	pair<T*, T*>
		alloc_n_copy(const T*, const T*);
	void free();        //銷燬元素並釋放記憶體
	void reallocate();   //獲得更多記憶體並拷貝已有元素
	void reallocate(size_t n);
};

//注意:靜態成員的類外定義。
template <typename T>
allocator<T> Vec<T>::alloc;

//建構函式實現
template <typename T>
inline
Vec<T>::Vec(initializer_list<T> &ls) {
	auto newdata = alloc_n_copy(ls.begin(), ls.end());
	elements = newdata.first;
	first_free = cap = newdata.second;
}

//如果沒有空間,則呼叫reallocate分配更多記憶體。
template <typename T>
inline void Vec<T>::chk_n_alloc(void) {
	if (size() == capacity())
		reallocate();
}
//分配記憶體,並拷貝一個給定範圍中的元素
template <typename T>
pair<T*, T*>
Vec<T>::alloc_n_copy(const T *b, const T *e)
{
	auto data = alloc.allocate(e - b);
	return { data, uninitialized_copy(b, e, data) };
}



//會銷燬構造的元素並釋放記憶體
template <typename T>
void Vec<T>::free() {
	//不能傳遞給deallocate一個空指標
	if (elements) {//逆序銷燬舊元素
		for (auto p = first_free; p != elements; /* 空 */)
			alloc.destroy(--p);
		alloc.deallocate(elements, cap - elements);
	}
}
/*
void StrVec::free(){
if (elements){
for_each(elements,first_free,[this](string &rhs) { alloc.destory(&rhs);});
alloc.deallocate(elements,cap-elements);
}
}
*/
template <typename T>
Vec<T>::Vec(const Vec &sv) {
	auto newdata = alloc_n_copy(sv.begin(), sv.end());
	elements = newdata.first;
	first_free = cap = newdata.second;
}

template <typename T>
Vec<T>::~Vec() {
	free();
}

template <typename T>
Vec<T>& Vec<T>::operator=(const Vec &sv) {
	auto newdata = alloc_n_copy(sv.begin(), sv.end());
	free();
	elements = newdata.first;
	first_free = cap = newdata.second;
	return *this;
}

template <typename T>
void Vec<T>::push_back(const T &s) {
	chk_n_alloc();      //確保有空間容納新元素
						//在first_free指向的元素中構造s的副本
	alloc.construct(first_free++, s);
}

template <typename T>
void Vec<T>::reallocate() {
	//我們將分配當前大小兩倍的記憶體空間
	auto newcapacity = (size() ? 2 * size() : 1);
	//分配新記憶體
	auto newdata = alloc.allocate(newcapacity);
	//將資料從舊記憶體移動到新記憶體
	auto dest = newdata;         //指向新陣列中下一個空閒位置
	auto elem = elements;        //指向舊陣列中下一個元素
	for (size_t i = 0; i != size(); ++i)
		alloc.construct(dest++, std::move(*elem++));
	free();           //一旦我們移動完元素就釋放舊記憶體空間
	elements = newdata;
	first_free = dest;
	cap = elements + newcapacity;
}

template <typename T>
void Vec<T>::reallocate(size_t n) {
	auto newdata = alloc.allocate(n);
	auto dest = newdata;
	auto elem = elements;
	for (size_t i = 0; i != size(); ++i)
		alloc.construct(dest++, std::move(*elem++));
	free();
	elements = newdata;
	first_free = dest;
	cap = newdata + n;
}

template <typename T>
void Vec<T>::resize(size_t n) {
	if (n > size()) {
		while (size() < n)
			push_back("");
	}
	else if (n < size()) {
		while (size() > n)
			alloc.destroy(--first_free);
	}
}

template <typename T>
void Vec<T>::reserve(size_t n) {
	if (n > capacity())
		reallocate(n);
}
#endif // ! VEC_H

16.17: 在模板引數列表中,typename和class兩個關鍵字的含義相同,都是表明模板引數是一個型別 當我們使用模板型別引數通過作用域運算子來訪問成員時,如T::value_type,預設情況下,c++語言假定通過作用域運算子訪問的是名字而不是型別。因此我們希望使用一個模板型別引數的型別成員,就必須通過使用關鍵字typename顯示告訴編譯器該名字是一個型別 16.18: (a)錯誤,缺少typename,改為:template <typename T, typename U, typename V> void f1(T, U, V); (b)錯誤,模板型別名不能作為變數名,改為:template <typename T> T f2(T &); (c)錯誤,內聯位置錯誤,改為:template <typename T> inline T foo(T, unsigned int*); (d)錯誤,缺少返回型別,改為:template <typename T> T f4(T, T); (e)合法 16.19:

#include <iostream>
#include <vector>
using namespace std;

template <typename T>
void print(ostream &os, const T &vec)
{
	for (typename T::size_type i = 0; i < vec.size(); ++i)
	{
		os << vec[i] << " ";
	}
	os << endl;
}
int main(int argc, char **argv)
{
	vector<int> vec{ 1,2,3,4,5,6 };
	print(cout, vec);
	system("pause");
        return 0;
}

16.20:

#include <iostream>
#include <vector>
using namespace std;

template <typename T>
void print(ostream &os, const T &vec)
{
	for (auto it = vec.begin(); it != vec.end();++it)
	{
		os << *it << " ";
	}
	os << endl;
}
int main(int argc, char **argv)
{
	vector<int> vec{ 1,2,3,4,5,6 };
	print(cout, vec);
	system("pause");
    return 0;
}

16.21:

#ifndef DEBUGDELETE_H
#define DEBUGDELETE_H

#include <iostream>
using namespace std;

class DebugDelete {
public:
	DebugDelete(ostream &s = cerr): os(s) { }
	template <typename T>
	void operator()(T *p) const
	{
		os << "delete unique_ptr" << endl;
		delete p;
	}
private:
	ostream & os;
};
#endif  //DEBUGDELETE_H
//test.cpp
#include <iostream>
#include "DebugDelete.h"
using namespace std;


int main(int argc, char **argv)
{
	double *p = new double;
	DebugDelete d;
	d(p);
	int *ip = new int;
	DebugDelete()(ip);
	system("pause");
        return 0;
}

16.22: shared_ptr過載刪除器的方式:

shared_ptr<int> n(new int,Debugdelete());

16.24:

#ifndef  BLOB_H
#define BLOB_H

template <typename T> class Blob {
	template <typename It> Blob(It &b, It &e);
	//....
};

template <typename T>
template <typename It>
Blob<T>::Blob(It &b, It &e) :
	data(make_shared<vector<T>>(b,e)) { }
#endif  //BLOB_H

16.25: 例項化宣告class vector 例項化定義class vector 16.26: 不可以,因為當我們顯示例項化vector<NoDefault>時,編譯器會例項化vector的所有成員函式,包括它接受容器大小引數的建構函式。vector的這個建構函式會使用元素的預設建構函式來對元素進行值初始化,而NoDefault沒有預設建構函式,從而導致編譯器報錯 16.27: (a)char例項化了模板stack (b)double例項化了模板stack (c)int例項化了模板stack (d)已有該例項化的定義,不需要再例項化 (e)已有該例項化的定義,不需要再例項化 (f)string例項化了模板stack

16.28:shared_ptr.......

#ifndef  MY_SHARED_PTR_H
#define  MY_SHARED_PTR_H

template <typename T> class My_shared_ptr {
public:
	My_shared_ptr() :
		p(nullptr), use(nullptr) {}
	//建構函式
	My_shared_ptr(T &pt) :
		p(pt), use(new size_t(1)) {}
	//拷貝建構函式
	My_shared_ptr(const My_shared_ptr &msp):
		p(msp.p), use(msp.use) {
		if (use) ++*use;
	}
	//拷貝賦值運算子
	My_shared_ptr& operator=(const My_shared_ptr&);
	//解構函式
	~My_shared_ptr();

	//介面函式
	T& operator*() { return *p; }
	T& operator*() const { return *p; }
private:
	T * p;    //指標
	size_t * use;  //儲存共享該物件的指標數目(即為引用計數)
};

//拷貝賦值運算子
template <typename T>
My_shared_ptr<T>&  My_shared_ptr<T>::operator=(const My_shared_ptr &rhs)
{
	if (rhs.use)           //如果賦值的右值的引用計數不為0則增加其引用計數
		++*(rhs.use);
	if (use && --*use == 0)  //如果賦值的左值的引用計數不為0但遞減引用計數為0則刪除該指標
	{
		delete p;
		delete use;
	}
	p = rhs.p;
	use = rhs.use;
	return *this;
}

//解構函式
template <typename T>
My_shared_ptr<T>::~My_shared_ptr()
{
	if (use && --*use == 0)
	{
		delete p;
		delete use;
	}
}
#endif // ! MY_SHARED_PTR_H

16.31:

unique_ptr是儲存刪除器函式的指標,所以需要一次跳轉操作,並不會內聯而是跳轉

16.32: 在模板實參推斷過程中,編譯器使用函式呼叫中的實參型別來尋找模板實參,用這些模板實參生成的函式版本與給定的函式呼叫匹配

16.33:

const轉換、陣列或函式指標轉換

16.34: 該題中的字串並不是string型別,而是根據長度判斷的char[n]型別,長度不同型別也不同,所以(a)不合法,(b)合法

16.35:

(a):合法,第一個為char,即為T,第二個為char [1],可以進行型別轉換

(b):合法,T為double,clac第二個引數為普通型別的int,可以進行算數型別轉換

(c):不合法,一個char,一個char[1]

(d):不合法,一個double一個float,無法進行型別轉換 16.36:

(a):合法,T為 int*

(b):合法,T1和T2都為int*

(c):合法,T為const int*

(d):合法,T1和T2都為const int*

(e):不合法,首先需要判斷實參的型別是否相同,再判斷型別是否可轉換,兩引數一個為const一個非const

(f):合法,T1為int*, T2的型別為const int*

16.37: 雖然max兩個實參不同,但是我們可以通過顯示的模版實參進行修改,指定了引數之後,傳入引數可以不同,但是轉換結果必須相同,可以進行型別轉換

max<int>(a,b);//將b的型別強制轉換為int

16.38: make_shared接收的實參是一個模板引數包,返回型別是一個shared_ptr<_Ty>,_Ty是一個模板引數,不指定顯示模板實參無法推斷其返回型別 16.39: char[]型別轉換為string

compare<string>("a","abb");

16.40:

函式合法。

但有兩個問題:

序列元素型別必須支援+運算子

*beg + 0是右值,因此fcn3的返回型別被推斷為元素型別的常量引用

16.41:

#ifndef SUM_H
#define SUM_H

template <typename T>
auto sum(const T&a, const T&b)->decltype(a + b)
{
	return a + b;
}
#endif //SUM_H
#include<iostream>
#include "Sum.h"
using namespace std;

int main(int argc, char **argv)
{
	int a = 123353;
	int b = 124568;
	cout << sum(a,b) << endl;
	system("pause");
        return 0;
}

16.42-16.45:

#include <iostream>
#include <vector>
using namespace std;

template <typename T> void g(T&& val){}

template <typename T> void g1(T val) {}

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

template <typename T> void g3(T&& val) { vector<T> v; }
int main(int argc, char **argv)
{
	int i = 0;
	const int ci = i;
	//16.42
	g(i);  //void g<int &>(int &val)
	g(ci); //void g<const int &>(const int &val)
	g(i * ci);  //void g<int> (int &&val)
	//16.43
	g(i = ci);  //賦值表示式是一個左值,void g<int &>(int &val)
	//16.44
	g1(i);   //void g1<int>(int val)
	g1(ci);  //頂層const被忽略,void g1<int>(int val)
	g1(i * ci);  //void g1<int>(int val)

	g2(i);  //void g2<int>(const int&)
	g2(ci);//void g2<int>(const int&)
	g2(i * ci); //void g2<int>(const int&)
	//16.45
	g3(42);//42為右值,void g2<int> (int &&val)
	g3(i); //當對一個int型別變數呼叫g3時,T為int &,但vector的元素不能是引用,因此編譯失敗
	system("pause");
        return 0;
}

16.46: 將elem的元素移動到dest空間(將資料從舊的記憶體空間移動到新記憶體空間)

16.47:

#ifndef  UP_DOWN_H
#define  UP_DOWN_H

template <typename F, typename T1, typename T2>
void flip(F f, T1 &&t1, T2 &&t2)
{
	f(std::forward<T2>(t2), std::forward<T1>(t1));
}

#endif // ! UP_DOWN_H
#include <iostream>
#include <utility>
#include "Up_down.h"
using namespace std;

void f(int &i, int &&j)
{
	cout << i << " " << j << endl;
}
int main(int argc, char **argv)
{
	int i = 5;
	flip(f, 42, i);
	system("pause");
        return 0;
}

16.48:

#ifndef  DEBUG_REP_H
#define  DEBUG_REP_H
#include <iostream>
#include <string>
using namespace std;

template <typename T> string debug_rep(const T &t)
{
	cout << "const T&: " << endl;
	return t.str();
}
template <typename T> string  debug_rep(T *t)
{
	cout << "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

16.49、16.50:

//16.49.h
#ifndef  TEST_H
#define  TEST_H

template <typename T> void f(T t)
{
	cout << "f : T t:" << t << endl;
}
template <typename T> void f(const T *t)
{
	cout << "f : const 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;
}
#endif // ! TEST_H
#include <iostream>
#include "16.49.h"
using namespace std;

template <typename T> void f(T);
template <typename T> void f(const T*);
template <typename T> void g(T);
template <typename T> void g(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*)
	system("pause");
        return 0;
}

16.51、16.52:

#include <iostream>
using namespace std;

template <typename T, typename...Args>
void foo(const T &t, const Args&...rest)
{
	cout << sizeof...(Args) << endl;
	cout << sizeof...(rest) << endl;
}
int main(int argc, char **argv)
{
	int i = 0;
	double d = 3.14;
	string s = "how now brown cow";
	foo(i, s, 42, d);  //3 3
	foo(s, 42, "hi"); //2 2
	foo(d, s);  //1 1
	foo("hi");//0 0
	system("pause");
        return 0;
}

16.53:

#include <iostream>
#include <string>
using namespace std;

template <typename T>
ostream &print(ostream &os, const T &t)
{
	return os << t;  //包中最後一個元素之後不列印分隔符
}

template <typename T, typename...Args>
ostream &print(ostream &os, const T &t, const Args&...rest)
{
	os << t << ", ";    //列印第一個實參
	return print(os, rest...);  //遞迴呼叫,列印其他實參
}
int main(int argc, char **argv)
{
	int i = 0;
	double d = 3.14;
	float m = 4.0;
	string s = "how now brown cow";
	print(cout, i, d);
	cout << endl;
	print(cout, i, d, m);
	cout << endl;
	print(cout, i, d, m, "nu", "ml", s);
	cout << endl;
	system("pause");
        return 0;
}

16.54:

缺少“<<”的定義

16.55:

在其後呼叫,先前的print版本找不到非可變引數版本的print函式,造成無限遞迴 16.56:

#include <iostream>
#include <string>
#include <sstream>
using namespace std;

template <typename T> string debug_rep(const T &t);
template <typename T> string debug_rep(T *p);
string debug_rep(const string &s);
string debug_rep(char *p);
string debug_rep(const char *p);

template <typename T> string debug_rep(const T &t)
{
	ostringstream ret;
	ret << t;
	return ret.str();
}
template <typename T> string debug_rep(T *p)
{
	ostringstream ret;
	ret << "pointer: " << p;
	if (p)
		ret << " " << debug_rep(*p);
	else
		ret << " null pointer";
	return ret.str();
}

string debug_rep(const string &s)
{
	return '"' + s + '"';
}

string debug_rep(char *p)
{
	return debug_rep(string(p));
}

string debug_rep(const char *p)
{
	return debug_rep(string(p));
}

template <typename T>
ostream &print(ostream &os, const T &t)
{
	return os << t;  //包中最後一個元素之後不列印分隔符
}

template <typename T, typename...Args>
ostream &print(ostream &os, const T &t, const Args&...rest)
{
	os << t << ", ";    //列印第一個實參
	return print(os, rest...);  //遞迴呼叫,列印其他實參
}

template <typename ...Args>
ostream &errorMsg(ostream &os, const Args&...rest)
{
	return print(os, debug_rep(rest)...);
}
int main(int argc, char **argv)
{
	string str = "c++";
	errorMsg(cout, str, "prime", 4, 4.8, "r");
	system("pause");
        return 0;
}

16.57: error_msg只能接受string型別的形參或者能轉換成string的實參;errorMsg能接受各個不同型別的實參,但errorMsg的編寫較為繁瑣

16.58:

template <typename...Args>
inline 
void StrVec::emplace_back(Args...args)
{
	chk_n_alloc();
	alloc.construct(first_free++, std::forward<Args>(args)...);
}

16.59:

s是一個左值,經過std::forward<string>(s),將一個型別為string&的實參傳遞給string的建構函式構造一個新元素 16.60: 接受可變引數模板,轉發其引數初始化一個內存於記憶體空間,返回一個shared_ptr 16.61:

template <typename T, typename ...Args>
shared_ptr<T> make_shared(Args&&...args)
{
	return shared_ptr<T>(new T(std::forward<Aegs>(args)));
}

16.62:

#ifndef SALES_DATA_H_INCLUDED
#define SALES_DATA_H_INCLUDED
 
#include<iostream>
class Sales_data{
    //友元函式
    friend struct std::hash<Sales_data>;
    friend std::istream& operator >> ( std::istream&, Sales_data& );
    friend std::ostream& operator << ( std::ostream&, const Sales_data& );
    friend bool operator < ( const Sales_data&, const Sales_data& );
    friend bool operator == ( const Sales_data&, const Sales_data& );
    friend Sales_data add( Sales_data &rhs, Sales_data &lhs );
    friend std::istream& read( std::istream& in, Sales_data &item );
    friend std::ostream& print( std::ostream &out, const Sales_data &rhs );
 
    public:  //建構函式
 /*       Sales_data( const std::string &book, const unsigned units, const double sellingprice, const double saleprice ) :
            bookNo( book ), units_sold( units ), selling_price( sellingprice ), sale_price( saleprice )
            {
                if( selling_price )
                    discount = sale_price / selling_price;
                std::cout << "四個引數的建構函式的函式體" << std::endl;
            }
*/
        //委託建構函式:
 /*       Sales_data(): Sales_data( "", 0, 0, 0 ) { std::cout << "無引數的建構函式的函式體" << std::endl; }
        Sales_data( const std::string &s ) : Sales_data( s, 0, 0, 0 ) { std::cout << "接受字串的建構函式的函式體" << std::endl; };
        Sales_data( std::istream &is ) : Sales_data() { read( is, *this ); std::cout << "接受輸入流的建構函式的函式體" << std::endl; }
*/
        Sales_data() = default;
        Sales_data( const std::string &book ): bookNo( book ) { }
        Sales_data( const std::string &book, const unsigned units, const double sellingprice, const double saleprice );
        Sales_data( std::istream &is ) { is >> *this; }
 
    public:
        Sales_data& operator += ( const Sales_data& );
        std::string isbn() const { return bookNo; }
        Sales_data& combine( const Sales_data &rhs );
 
    private:
        std::string bookNo;
        unsigned units_sold = 0;
        double selling_price = 0.0;
        double sale_price = 0.0;
        double discount = 0.0;
};
 
 
inline bool compareIsbn( const Sales_data&lhs, const Sales_data&rhs )
{
    return lhs.isbn() == rhs.isbn();
}
 
Sales_data operator + ( const Sales_data&, const Sales_data& );//宣告
 
//友元函式定義
inline bool operator == ( const Sales_data &lhs, const Sales_data &rhs )
{
    return lhs.units_sold == rhs.units_sold && lhs.sale_price == rhs.sale_price &&
            lhs.isbn() == rhs.isbn();
}
 
inline bool  operator != ( const Sales_data &lhs, const Sales_data &rhs )
{
    return !( lhs == rhs );
}
 
inline bool operator<( const Sales_data &lhs, const Sales_data &rhs )
{
    return lhs.bookNo < rhs.bookNo;
}
 
inline Sales_data add( Sales_data &rhs, Sales_data &lhs )
{
    Sales_data sum = rhs;
    sum.combine( lhs );
    return sum;
}
inline std::ostream& print( std::ostream &out, const Sales_data &rhs )
{
    out << rhs.bookNo << " " << rhs.units_sold << " " << rhs.selling_price << " "
        << rhs.sale_price << " " << rhs.discount;
    return out;
}
 
inline std::istream& read( std::istream& in, Sales_data &item )
{
 
 
    in >> item.bookNo >> item.units_sold >> item.selling_price >> item.sale_price;
    return in;
}
 
//建構函式
Sales_data::Sales_data( const std::string &book, unsigned units, double sellingprice, double saleprice )
{
    bookNo = book;
    units_sold = units;
    selling_price = sellingprice;
    sale_price = saleprice;
    if( selling_price != 0 )
        discount = sale_price / selling_price;
}
 
 
 
//成員函式
Sales_data& Sales_data::operator += ( const Sales_data &rhs )
{
    sale_price = ( rhs.sale_price * rhs.units_sold + sale_price * units_sold )
                /( rhs.units_sold + units_sold );
    selling_price = ( rhs.selling_price * rhs.units_sold + selling_price * units_sold )
                /( rhs.units_sold + units_sold );
    units_sold += rhs.units_sold;
 
    if( sale_price != 0 )
        discount = sale_price / selling_price;
 
    return *this;
}
 
 
Sales_data operator + ( const Sales_data &lhs, const Sales_data &rhs )
{
    Sales_data tmp( lhs );
    tmp += rhs;
 
    return tmp;
}
 
 
std::istream& operator >> ( std::istream &in, Sales_data &s )
{
    in >> s.bookNo >> s.units_sold >> s.selling_price >> s.sale_price;
 
    if( in && s.selling_price != 0 )
        s.discount = s.sale_price / s.selling_price;
    else
        s = Sales_data();
 
    return in;
}
 
 
std::ostream& operator << ( std::ostream &out, const Sales_data &s )
{
    out << s.isbn() << " " << s.units_sold << " "
        << s.selling_price << " " << s.sale_price << " " << s.discount;
 
    return out;
}
 
Sales_data& Sales_data::combine( const Sales_data &rhs )
{
    units_sold += rhs.units_sold;
    sale_price = ( units_sold * sale_price + rhs.units_sold * rhs.sale_price )
                / ( units_sold + rhs.units_sold );
    if( selling_price != 0 )
        discount = sale_price / selling_price;
 
    return *this;
}
 
namespace std{
    template <> struct hash<Sales_data>{
        typedef size_t result_type;
        typedef Sales_data argument_type;
 
        size_t operator()( const Sales_data & s ) const;
    };
 
    size_t hash<Sales_data>::operator() ( const Sales_data &s ) const{
        return hash<string>()( s.bookNo ) ^
            hash<unsigned>() ( s.units_sold ) ^
            hash<double>() ( s.selling_price ) ^
            hash<double>() ( s.sale_price );
    }
}
 
#endif // SALES_DATA_H_INCLUDED
#include<iostream>
#include<unordered_set>
#include"Sales_data.h"
 
 
using namespace std;
 
int main()
{
    unordered_multiset<Sales_data> SDset;
    Sales_data obj1( "C++Primer", 50, 128, 68 );
    Sales_data obj2( "C primer plus", 40, 98, 52 );
    Sales_data obj3( obj1 );
 
    SDset.insert( obj1 );
    SDset.insert( obj2 );
    SDset.insert( obj3 );
 
    for( const auto &sd : SDset )
        cout << sd << endl;
    return 0;
}

16.63、16.64:

#include <iostream>
#include <vector>
using namespace std;

template <typename T>
void count_num(const vector<T> &vec, const T &val)
{
	int cnt = 0;
	for (auto it = vec.begin(); it != vec.end(); ++it)
	{
		if (*it == val)
		{
			++cnt;
		}
	}
	cout <<"出現的次數為:" << cnt << endl;
}

template <>
void count_num(const vector<const char*> &vec, const char* const &val)
{
	int cnt = 0;
	for (auto it = vec.begin(); it != vec.end(); ++it)
	{
		if (strcmp((*it), val) == 0) {
			++cnt;
		}
	}
	cout << "出現的次數為:" << cnt << endl;
}
int main(int argc, char **argv)
{
	vector<double> vec1{ 1.2,2.3,3.4,4.5,5.6,6.7 };
	count_num(vec1, 2.3);
	vector<int> vec2{ 1,2,3,3,5,6 };
	count_num(vec2, 3);
	vector<string> vec3{ "a","ax","ax","ax","axca" };
	string s = "ax";
	count_num(vec3, s);
	vector<const char*> vec4{ "1","12","123","1234" };
	const char* c = "123";
	count_num(vec4, c);
	system("pause");
        return 0;
}

16.65:

template <typename T> string debug_rep(const T &t)
{
	ostringstream ret;
	ret << t;
	return ret.str();
}
template<>
string debug_rep(char * const &p)
{
	return debug_rep(string(p));
}
template<>
string debug_rep(const char * const &p)
{
	return debug_rep(string(p));
}

16.66: 優點:特例化只是一個例項,並不是過載,所以會減少過載函式的資源消耗 缺點:當有函式過載時,優先順序會低於過載函式 16.67: 特例化的本質是例項化一個模板,而非過載它,因此特例化並不影響函式匹配