16.1節練習

14396 ワード

練習16.1はインスタンス化の定義を与える.
コンパイラは、推定されたテンプレートパラメータを使用して、特定のバージョンの関数をインスタンス化します.
16.2自分のバージョンのcompare関数を作成してテストする練習をします.
#include 
using namespace std;
template 
int compare(const T &v1, const T &v2)
{
	if (v1 < v2) { return -1; }
	if (v2 < v1) { return 1; }
}
int main()
{
	int x = 2, y = 1;
	cout << compare(x, y) << endl;
}

練習16.3対2 Sales_Dataオブジェクトはcompare関数を呼び出し、コンパイラがインスタンス化中にエラーをどのように処理するかを観察します.
コンパイラが見つかりません
16.4動作が標準ライブラリfindアルゴリズムに類似するテンプレートを記述することを練習する.関数には2つのテンプレートタイプパラメータが必要です.1つは関数を表す反復器パラメータで、もう1つは価値のあるタイプを表します.関数を使用してvectorとlistで指定した値を検索します.
#include 
#include 
#include 
#include 
#include 
using namespace std;
template 
iterator findd(const iterator &begin, const iterator &end, const T &val)
{
	for (iterator iter = begin; iter != end; ++iter) {
		if (*iter == val) {
			return iter;
		}
	}
	return end;
}
int main()
{
	
	vector ivec{ 1,3,15,24 };
	string s1("rose");
	string s2("paul");
	list slist{s1,s2 };
	
	auto iter = findd(ivec.begin(), ivec.end(), 3);
	auto ster = findd(slist.begin(), slist.end(), "paul");
	cout << *iter << endl;
	cout << *ster << endl;
}

練習16.5は6.2.4節(195ページ)のprint関数のテンプレートタイプを記述し、任意のサイズ、任意の要素タイプの配列を処理できる配列の参照を受け入れる.
#include 
using namespace std;
template 
void print(const T (&arr)[num])
{
	for (auto &i : arr) {
		cout << i << " ";
	}
	cout << endl;
}
int main()
{
	int val[] = { 1,2,3,4,5,6,7,9,7,8 };
	print(val);
	
	char* s[] = { "ss","dd" };
	print(s);
}

練習16.6配列の実パラメータを受け入れる標準ライブラリ関数beginとendがどのように動作していると思いますか.自分のバージョンのbeginとendを定義します.
#include 
#include 
using namespace std;
template 
T* beginn( T(&arr)[num]) 
{
	cout << "useing begin" << endl;
	return arr; 
}
template 
T* endd( T(&arr)[num])
{
	cout << "using end" << endl;
	return arr + num;
}
int main()
{
	int val[] = { 1,2,3,4,5,6,7,8,9 };
	cout << *beginn(val) << endl;
	cout << *(endd(val)-1) << endl;
}

練習16.7 constexprテンプレートを作成し、所与の配列のサイズを返します.
#include 
using namespace std;
template 
constexpr std::size_t coutt(T(&arr)[num])
{
	return num;
}
int main()
{
	int val[] = { 1,2,4,3,4,6 };
	cout << coutt(val) << endl;
}

練習16.8 97ページの「肝心な概念」では、C++プログラマーが愛用していることに気づきました!=好きじゃない
オブジェクト向けにプログラミングする場合、クラスは必ずしも定義されません.
練習16.9関数テンプレートとは何ですか.クラステンプレートとは何ですか.
各タイプに新しい関数を定義するのではなく、共通の関数テンプレートを定義することができます.関数テンプレートは、特定のタイプの関数バージョンを生成するために使用できる式です.
クラステンプレートは、クラスを生成するための青写真です.コンパイラは、クラステンプレートのテンプレートパラメータタイプを推定することはできません.クラステンプレートを使用するには、テンプレートパラメータの代わりにテンプレート名の後のカッコに追加情報を入力する必要があります.
練習16.10クラステンプレートがインスタンス化されると、何が起こりますか?
クラステンプレートの各インスタンスは、独立したクラスを形成することができます.他の異なるインスタンス化では、特別なアクセス権はありません.
練習16.11次のLIstの定義を修正します.
変更後のバージョンを指定します.
template  class ListItem;  
template  class List{  
public:  
  List();  
  List(const List &);  
  List& operator=(const List &);  
  ~List();  
  void insert(ListItem *ptr, elemType value);  
private:  
  ListItem *front, *end;  
}; 
Listはクラス役割ドメインにおいてListと省略することができる.
練習16.12自分のバージョンのBlobとBlobPtrテンプレートを作成し、本に定義されていない複数のconstメンバーを含む.
#include 
#include 
#include 
#include 
#include 
using namespace std;
template  class Blob {
public:
	typedef T value_type;
	typedef typename vector::size_type size_type;
	Blob() :data(make_shared>()) {}
	Blob(initializer_list il) :data(make_shared>(il)) {}
	size_type size() const { return data->size(); }
	bool empty() const { return data->empty(); }
	void push_back(T &&t) { data->push_back(std::move(t)); }
	void pop_back();
	T& back();
	T& operator[](size_type i);
private:
	shared_ptr> data;
	void check(size_type i, const string &msg) const;
};
template 
void Blob::check(size_type i, const string &msg) const
{
	if (i >= data->size()) {
		throw out_of_range(msg);
	}
}
template 
T& Blob::back()
{
	check(0, "back on emppty Blob");
	return data->back();
}
template 
T& Blob::operator[](size_type i)
{
	check(i, "subscript out of range");
	return (*data)[i];
}
template 
void Blob::pop_back()
{
	check(0, "pop_back on empty Blob");
	data->pop_back();
}
template  class BlobPtr {
	friend bool operator==(const BlobPtr&, const BlobPtr&);
public:
	BlobPtr() :curr(0) {}
	BlobPtr(Blob &a, size_t sz = 0) :wptr(a.data), curr(sz) {}
	T& operator*()const;
	BlobPtr& operator++();
	BlobPtr& operator--();
private:
	shared_ptr> check(size_t, const string&)const;
	weak_ptr> wptr;
	size_t curr;
};
template
shared_ptr> BlobPtr::check(size_t i, const string &msg) const
{
	auto ret = wptr.lock();
	if (!ret) {
		throw runtime_error("unbound BlobPtr");
	}
	if (i >= wptr->size()) {
		throw out_of_range(msg);
	}
	return ret;

}
template
T& BlobPtr::operator* ()const
{
	auto p = check(curr, "dereference past end");
	return (*p)[curr];
}
template 
BlobPtr& BlobPtr::operator++()
{
	check(curr, "incremnet past end of BlobPtr");
	++curr;
	return *this;
}
template 
BlobPtr& BlobPtr::operator--()
{
	--curr;
	check(curr, "decrement past begin of BlobPtr");
	return *this;
}
template
operator ==(const BlobPtr &lhs, const BlobPtr &rhs)
{
	return lhs.wptr == rhs.wptr&&lhs.curr == rhs.curr;
}

練習16.13 BlobPtrの等和関係演算子にどのタイプの友好関係を選択するかを説明します.
インスタンス化と一致するタイプ.
練習16.14 Screenクラステンプレートを作成し、非タイプパラメータでScreenの高さと幅を定義します.
#include 
#include 
#include 
using namespace std;
template  class Screen {
	friend istream& operator >> (istream&, Screen&);
	friend ostream& operator << (ostream&, const Screen&);
public:
	Screen() = default;
	Screen(T h, T w) :height(h), width(w) {}
	Screen(T h, T w, char c) :height(h), width(w), contents(w*h, c) {}

	char get() const { return contents[currsor]; }
	char get(T h, T w)const { T row = h*w; return contents[row + w]; }
	Screen& move(T, T);
	Screen& set(char);
	Screen& set(T, T, char);
	Screen& display(ostream &os) { do_display(os); return *this; }
	const Screen& display(ostream &os)const { do_display(os); return *this; }
private:
	T hegiht;
	T width;
	T currsor;
	std::string contents;
	void do_display(ostream &os)const { os << contents; }
};
template 
Screen& Screen::move(T r, T c)
{
	T row = r*c;
	currsor = row + c;
	return *this;
}
template 
Screen& Screen::set(char c)
{
	contents[currsor] = c;
	return *this;
}
template 
Screen& Screen::set(T r, T c, char ch)
{
	contents[r*c + c] = ch;
	return *this;
}
template 
istream& operator >> (istream &is, Screen &item)
{
	is >> item.hegiht >> item.width;
	if (!is) {
		item = Screen();
	}
	return is;
}
template 
ostream& operator >> (ostream &os, const Screen& item)
{
	os << item.hegiht << " " << item.width << " " << item.contents;
	return os;
}

練習16.15上記の問題です.
練習16.16 StrVecクラス(465ページ参照)をテンプレートに書き換え、Vecと命名する.
#include 
#include 
#include 
#include 
using namespace std;
template class Vec {
public:
	Vec() :elements(nullptr), first_free(nullptr), cap(nullptr) {}
	Vec(const Vec&);
	Vec &operator=(const Vec&);
	~Vec();
	void push_back(const T&);
	size_t size() const { return first_free - elements; }
	size_t capacity() const { return cap - elements; }
	T *begin()const { return elements; }
	T *end()const { return first_free; }
private:
	static allocator alloc;
	void chk_n_alloc() { if (size() == capacity()) reallocate(); }
	pair alloc_n_copy(const T*, const T*);
	void free();
	void reallocate();
	T *elements;
	T *first_free;
	T *cap;
};
template 
void Vec::free()
{
	if (elements) {
		for (auto p = first_free; p != elements;) {
			alloc.deallocate(elements, cap - elements);
		}
	}
}
template 
void Vec::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 
pair Vec::alloc_n_copy(const T *b, const T *e)
{
	auto data = alloc.allocate(e - b);
	return{ data,uninitialized_copy(b,e,data) };
}
template 
Vec::Vec(const Vec &item)
{
	auto newdata = alloc_n_copy(item.begin(), item.end());
	elements = newdata.first;
	first_free = cap = newdata.second;
}
template 
Vec& Vec::operator=(const Vec &item)
{
	auto data = alloc_n_copy(item.begin(), item.end());
	free();
	elements = data.first;
	first_free = cap = data.second;
	return *this;
}
template 
Vec::~Vec()
{
	free();
}
template 
void Vec::push_back(const T &s)
{
	chk_n_alloc();
	alloc.construct(first_free++, s);
}

練習16.17 typenameと宣言されたタイプとclassと宣言されたタイプパラメータの違いは何ですか(もしあれば)?typenameはいつ使わなければなりませんか?
何の違いもない.
オブジェクトがタイプであるか、データ・メンバーであるかを区別する際にtypename、C++を使用すると、デフォルトでは、役割ドメイン・アクセサによってアクセスされる名前がタイプではないと仮定します.
コンパイラに名前表示タイプを通知する場合は、classではなくキーワードtypenameを使用する必要があります.
練習16.18次の各関数テンプレートの宣言を説明し、合法かどうかを指摘します.あなたが発見したすべての間違いを訂正します.
(a) template  vpod f1(T, U, V);	// typename U
(b) template  T f2(int &T);		// (int&)   (T&)
(c) inline template  T foo(T, unsigned int*);	//inline 
(d) template  f4(T, T);		// 
(e) typedef char Ctype;
	template  Ctype f5(Ctype a);	// 

練習16.19関数を記述し、コンテナの参照を受け入れ、コンテナの要素を印刷します.コンテナを使用するsize_typeとsizeメンバーは、印刷要素のループを制御します.
#include 
#include 
using namespace std;
template 
void func(const vector& data)
{
	typedef typename vector::size_type size_type;
	size_t size = data.size();
	for (size_t i = 0; i != size; ++i) {
		cout << data[i] << " ";
	}
	cout << endl;
}
int main()
{
	vector ivec = { 0,1,2,3,4,5,6,7,8,9 };
	func(ivec);
}

練習16.20前の問題の関数を書き直し、begin個endで返される反復器を使用してループを制御します.
#include 
#include 
using namespace std;
template 
void func(const vector& data)
{
	for (auto iter = data.cbegin(); iter != data.cend(); ++iter) {
		cout << *iter << " ";
	}
	cout << endl;
}
int main()
{
	vector ivec = { 0,1,2,3,4,5,6,7,8,9 };
	func(ivec);
}

練習16.21自分のDebugDeleteバージョンを作成します.
#include 
using namespace std;
class DebugDelete {
public:
	DebugDelete(ostream &s = cerr) :os(s) {}
	template void operator()(T* p)const
	{
		os << "deleting unique_ptr" << endl;
		delete p;
	}
private:
	ostream &os;
};

練習16.24はあなたのBlobテンプレートに2つの反復器を受け入れる構造関数を追加します.
template Blob(It b, It e) :data(make_shared>(b, e)) {}
クラス内で定義されます.
練習16.25次の宣言の意味を説明します.
extern template class vector;	// 
template class vector;		// 

練習16.26 NoDefaultがデフォルトのコンストラクション関数のないクラスであると仮定し、vectorを明示的にインスタンス化できますか?できないなら、なぜか説明します.
いけません.
クラステンプレートのインスタンス定義は、変更テンプレートのすべてのメンバーをインスタンス化します.
デフォルトのコンストラクション関数を定義せずにvectorをインスタンス化すると、削除した関数を参照しようとするエラーが発生します.
練習16.27次のラベル付き文ごとに、何がどのようなインスタンス化されているかを説明します(もしあれば).テンプレートがインスタンス化されると、なぜかを説明します.インスタンス化されていない場合は、なぜないのかを説明します.
#include 
using namespace std;
template  class Stack{};
void f1(Stack);					//(a)  Stack
class Exercise {	
	Stack &rsd;					//(b)  Stack
	Stack si;						//(c)  Stack
};
									
int main()
{
	Stack *sc;					//(d)  , 
	f1(*sc);							//(e)  。
	int iobj = sizeof(Stack);	//(f)  Stack
}