Contents
- Chapter 16 Template
- homework
- Test 16.1
- Test 16.3
- Test 16.4
- Test 16.5
- Test 16.6
- Test 16.7
- Test 16.12
- Test 16.14 - Test 16.15
- Test 16.19
- Test 16.20
- Test 16.21
- Test 16.22
- Test 16.24
- Test 16.26
- Test 16.38
- Test 16.39
- Test 16.41
- Test 16.46
- Test 16.47
- Test 16.48
- Test 16.52
- Test 16.53
- Test 16.56
- Test 16.58
- Test 16.59
- Test 16.60
- Test 16.61
- Test 16.62
- Test 16.63
- Test 16.64
- Test 16.65
Chapter 16 Template
homework
Test 16.1
template<typename T>
int compare(const T& v1, const T& v2)
{
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
Test 16.3
错误C2676二进制“<”:“const T”不定义该运算符或到预定义运算符可接收的类型的转换
Test 16.4
template<typename T, typename U>
T find2(const T& it1, const T& it2, const U& value)
{
for (; it1 != it2; ++it1)
if (*it1 == value)
return it1;
return it2;
}
int main()
{
vector<int> ivec{ 1, 2, 3, 4, 5, 6 };
list<string> slst{ "aaa", "bbb", "ccc", "ddd" };
auto it = find2(ivec.begin(), ivec.end(), 3);
auto it2 = find2(slst.begin(), slst.end(), "ccc");
cout << *it << " " << *it2;
}
Test 16.5
template<unsigned N>
void print(int(&arr)[N])
{
for (auto elem : arr)
cout << elem << endl;
}
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
print(arr);
}
Test 16.6
template<unsigned N, typename T>
T* my_begin(T(&arr)[N])
{
return arr;
}
template<unsigned N, typename T>
T* my_end(T(&arr)[N])
{
return arr + N - 1;
}
Test 16.7
template<unsigned N, typename T>
constexpr unsigned getSize(T(&arr)[N])
{
return N;
}
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
cout << getSize(arr) << endl;
}
Test 16.12
定义一个类一般都是在头文件.h中进行声明,在.cpp文件中实现,但使用模板类时应注意目前的C++编译器还无法分离编译,最好将实现代码和声明代码均放在头文件中。
Blob.h
#pragma once
#include<initializer_list>
#include<memory>
#include<string>
#include<vector>
#include<stdexcept>
using namespace std;
//statement
template <typename> class BlobPtr;
template <typename> class Blob;
template <typename T> bool operator==(const Blob<T>&, const Blob<T>&);
template <typename T> class Blob
{
typedef typename vector<T>::size_type size_type;
friend class BlobPtr<T>;
friend bool operator==<T>(const Blob<T>&, const Blob<T>&);
public:
Blob() : data(make_shared<vector<T>>()) {};
Blob(initializer_list<T> il) : data(make_shared<vector<T>>(il)) {};
template <typename U> Blob(const U& beg, const U& end) : data(make_shared<vector<T>>(beg, end)) {};
// elements number in Blob
size_type size() const { return data->size(); };
bool empty() const { return data->empty(); };
// add and delete element
void push_back(const T& t) { data->push_back(t); };
void push_back(T&& t) { data->push_back(std::move(t)); };
void pop_back();
// access element
T& front();
const T& front() const;
T& back();
const T& back() const;
T& operator[](size_type i);
const T& operator[](size_type i) const;
private:
shared_ptr<vector<T>> data;
void check(size_type i, const string& msg) const;
};
template <typename T> class BlobPtr
{
public:
BlobPtr() : curr(0) {};
BlobPtr(Blob<T>& a, size_t sz = 0) : wptr(a.data), curr(sz) {};
T& operator*() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; };
BlobPtr& operator++();
BlobPtr& operator--();
private:
shared_ptr<vector<T>> check(size_t, const string&) const;
weak_ptr<vector<T>> wptr;
size_t curr;
};
// Blob
template <typename T>
void Blob<T>::check(size_type i, const string& msg) const
{
if (i >= data->size())
throw out_of_range(msg);
}
// add and delete element
template <typename T>
void Blob<T>::pop_back()
{
check(0, "pop_back on empty strblob");
return data->pop_back();
}
// access element
template <typename T>
T& Blob<T>::front()
{
check(0, "front on empty strblob");
return data->front();
}
template <typename T>
const T& Blob<T>::front() const
{
check(0, "front on empty strblob");
return data->front();
}
template <typename T>
T& Blob<T>::back()
{
check(0, "front on empty strblob");
return data->back();
}
template <typename T>
const T& Blob<T>::back() const
{
check(0, "front on empty strblob");
return data->back();
}
template <typename T>
T& Blob<T>::operator[](size_type i)
{
check(i, "out of range");
return (*data)[i];
}
template <typename T>
const T& Blob<T>::operator[](size_type i) const
{
check(i, "out of range");
return (*data)[i];
}
// non-member interface function
template <typename T>
bool operator==(const Blob<T>& lhs, const Blob<T>& rhs)
{
return lhs.data == rhs.data;
}
// BlobPtr
template <typename T>
BlobPtr<T>& BlobPtr<T>::operator++()
{
check(curr, "increment past end of strblobptr");
++curr;
return *this;
}
template <typename T>
BlobPtr<T>& BlobPtr<T>::operator--()
{
--curr;
check(curr, "decrement past begin of strblob");
return *this;
}
Test 16.14 - Test 16.15
template <unsigned W, unsigned H> class Screen;
template <unsigned W, unsigned H> ostream& operator<<(ostream&, const Screen<W, H>&);
template <unsigned W, unsigned H> istream& operator>>(istream&, Screen<W, H>&);
template <unsigned W, unsigned H> class Screen
{
template <unsigned W, unsigned H> friend ostream& operator<<(ostream&, const Screen<W, H>&);
template <unsigned W, unsigned H> friend istream& operator>>(istream&, const Screen<W, H>&);
public:
Screen() : contents(W * H, ' ') {};
Screen(const char& c) : contents(W* H, c) {};
Screen& set(const char& c) { contents = string(W * H, c); return *this; };
private:
string contents;
};
template <unsigned W, unsigned H>
ostream& operator<<(ostream& os, const Screen<W, H>& screen)
{
os << screen.contents << endl;
return os;
}
template <unsigned W, unsigned H>
istream& operator>>(istream& is, Screen<W, H>& screen)
{
char c;
is >> c;
screen.set(c);
return is;
}
Test 16.19
template<typename T>
void printi(const T& target)
{
typedef typename T::size_type size_type;
for (size_type i = 0; i < target.size(); ++i)
cout << target[i] << " ";
cout << endl;
}
Test 16.20
template<typename T>
void printj(const T& target)
{
for (auto beg = target.begin(); beg != target.end(); ++beg)
cout << *beg << ' ';
cout << endl;
}
Test 16.21
class DebugDelete
{
public:
DebugDelete(ostream& s = cerr) : os(s) {};
template<typename T> void operator()(T* p) { os << "delete pointer" << endl; delete p; };
private:
ostream& os;
};
Test 16.22
把原来的TextQuery::TextQuery(ifstream& is)
构造函数定义改成:
TextQuery::TextQuery(ifstream& is) : file(new vector<string>, DebugDelete())
{
//.......
}
Test 16.24
template <typename T> class Blob
{
public:
template <typename U> Blob(const U& beg, const U& end) : data(make_shared<vector<T>>(beg, end)) {};
private:
shared_ptr<vector<T>> data;
void check(size_type i, const string& msg) const;
};
Test 16.26
不可以,当我们显式实例化 vector 时,编译器会实例化 vector 的所有成员函数,包括它接受容器大小参数的构造函数。vector 的这个构造函数会使用元素类型的默认构造函数来对元素进行值初始化,而 NoDefault 没有默认构造函数,从而导致编译错误。
Test 16.38
make_shared需要传递一个动态内存的值,并返回一个指向该值的指针。由于返回类型中包含模板参数,因此需要显式指定实参类型
Test 16.39
template<typename T>
int compare(const T& v1, const T& v2)
{
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
int main()
{
cout << compare<string>("aaa", "bbb");
return 0;
}
Test 16.41
template<typename T1, typename T2>
auto sum(const T1& v1, const T2& v2) -> decltype(v1 + v2)
{
return v1 + v2;
}
Test 16.46
若第二个参数是一个左值,则进行拷贝动作。但在上面的代码中,用 std::move 将一个左值转换为右值引用,这样,construct 会调用 string 的移动构造函数将数据从旧内存空间移动而不是拷贝到新的内存空间中,避免了不必要的数据拷贝操作。
Test 16.47
template<typename F, typename T1, typename T2>
void flip(F f, T1 &&t1, T2 &&t2)
{
f(std::forward<T2>(t2), std::forward<T1>(t1));
}
void g(int&& i, int& j)
{
cout << i << ' ' << j << endl;
}
int main()
{
int i = 0;
flip(g, i, 42);
return 0;
}
Test 16.48
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)
cout << " " << debug_rep(*p) << endl;
else
cout << " null pointer" << endl;
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));
}
int main()
{
string str("aaaa");
string* p = &str;
cout << debug_rep(p);
return 0;
}
Test 16.52
template<typename T, typename... Args>
void foo(const T& t, const Args&... rest)
{
cout << sizeof...(Args) << endl;
cout << sizeof...(rest) << endl;
}
int main()
{
int i = 0; double d = 3.14; string s = "what can i say";
foo(i, s, d, 42);
return 0;
}
Test 16.53
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 i = 0; double d = 3.14; string s = "what can i say";
print(cout, i, s, d, 42);
return 0;
}
Test 16.56
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)
cout << " " << debug_rep(*p) << endl;
else
cout << " null pointer" << endl;
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 i = 0; double d = 3.14; string s = "what can i say";
errorMsg(cout, i, s, d, 42);
return 0;
}
Test 16.58
class StrVec
{
//...
public:
template<typename...Args> void emplace_back(Args&&...);
//...
};
template<typename...Args>
void StrVec::emplace_back(Args&&... args)
{
chk_n_alloc();
alloc.construct(first_free++, std::forward<Args>(args)...);
}
Test 16.59
由于 s 是一个左值,经过包扩展,它将以如下形式传递给 construct。
std::forward<string>(s)
std::forward<string>
的结果类型是 string&
,因此,construct 将得到一个左值引用实参,它继续将此参数传递给 string 的拷贝构造函数来创建新元素。
Test 16.60
make_shared 的工作过程类似 emplace_back。
它接受参数包,经过扩展,转发给 new,作为 vector 的初始化参数。
Test 16.61
template <typename T, class... Args>
SharedPtr<T> make_shared(Args&&... args) {
SharedPtr<T> s(new T(std::forward<Args>(args)...));
return s;
}
Test 16.62
my_sales_data.h中
#ifndef MY_SALES_DATA_H
#define MY_SALES_DATA_H
#include<iostream>
#include<string>
template <class T> struct std::hash;
class my_sales_data
{
friend std::istream& operator>>(std::istream&, my_sales_data&);
friend std::ostream& operator<<(std::ostream&, const my_sales_data&);
friend my_sales_data operator+(const my_sales_data&, const my_sales_data&);
friend bool operator==(const my_sales_data&, const my_sales_data&);
friend bool operator!=(const my_sales_data&, const my_sales_data&);
friend struct std::hash<my_sales_data>;
public:
//构造函数(声明)
//my_sales_data() : units_sold(0), revenue(0.0) {}; //使用类内初始值
my_sales_data(std::string s) : bookNo(s) {};
my_sales_data(std::string s, unsigned n, double r) : bookNo(s), units_sold(n), revenue(r) { };
my_sales_data() : my_sales_data("1001", 0, 0) { }; //使用委托构造函数
my_sales_data(std::istream&);
inline double avg_price() const;
std::string isbn() const { return bookNo; }
my_sales_data& operator+=(const my_sales_data&);
my_sales_data& operator=(const std::string&);
operator std::string() const { return bookNo; };
operator double() const { return revenue; };
private:
//成员变量
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
//hash函数的特例化版本,用于对my_sales_data对象使用无序容器
namespace std
{
template<>
struct hash<my_sales_data>
{
typedef size_t result_type;
typedef my_sales_data argument_type;
size_t operator() (const my_sales_data& s) const;
};
inline
size_t hash<my_sales_data>::operator() (const my_sales_data& s) const
{
return hash<string>()(s.bookNo) ^ hash<unsigned>()(s.units_sold) ^ hash<double>()(s.revenue);
}
}
#endif
main.cpp
int main()
{
unordered_multiset<my_sales_data> mySet;
my_sales_data data1("1001", 2, 19.99);
my_sales_data data2("1002", 4, 49.99);
my_sales_data data3("1003", 2, 79.99);
mySet.insert(data1);
mySet.insert(data2);
mySet.insert(data3);
for (auto& s : mySet)
cout << s << endl;
return 0;
}
Test 16.63
template<typename T>
void count(const vector<T>& vec, T val)//这里注意val不能是引用,这样的话字符串字面值便不可传入(左值引用不能绑定到一个右值)
{
int times = 0;
for (auto& i : vec)
if (i == val)
++times;
cout << val << " occurs " << times << endl;
}
int main()
{
vector<int> ivec{ 1, 1, 2, 3, 3, 3, 4 };
vector<double> dvec{ 1.99, 2.99, 2.99, 3.99 };
vector<string> svec{ "aaa", "bbb", "ccc", "ccc" };
count(ivec, 1);
count(dvec, 2.99);
string s1("ccc");
count(svec, s1);
return 0;
}
Test 16.64
template<typename T>
void count(const vector<T>& vec, T val)//这里注意val不能是引用,这样的话字符串字面值便不可传入(左值引用不能绑定到一个右值)
{
int times = 0;
for (auto& i : vec)
if (i == val)
++times;
cout << val << " occurs " << times << endl;
}
template<>
void count(const vector<const char*> & vec, const char* val)
{
int times = 0;
for (auto& i : vec)
if (i == val)
++times;
cout << val << " occurs " << times << endl;
}
int main()
{
vector<const char*> cvec{ "aaa", "bbb", "ccc", "ccc", "ccc"};
count(cvec, "ccc");
return 0;
}
Test 16.65
template<> string debug_rep(char* p)
{
return debug_rep(string(p));
}
template<> string debug_rep(const char* p)
{
return debug_rep(string(p));
}