Exercise 16.1

1
2
3
// instantiate
// generate a spedified version of function or class based on template and passed in template arguments.

Exercise 16.2

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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()
{
  compare(1, 2);
  // compare(Sales_data(), Sales_data());
  // compiler error:
  // ...
  // no match for 'operator<' ...
}

Exercise 16.3

1
2
// See 16.2.cpp

16.2.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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()
{
  compare(1, 2);
  // compare(Sales_data(), Sales_data());
  // compiler error:
  // ...
  // no match for 'operator<' ...
}

Exercise 16.4

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
template <typename It, typename T2>
It new_find(It it1, It it2, const T2 &var)
{
  while(it1 != it2)
    {
      if(*it1 == var)
      return it1;
      else
      ++it1;
    }
  return it2;
}
int main()
{
  vector<int> vi = {1, 2, 3};
  list<int> li = {1, 2, 3};
  cout << *new_find(vi.begin(), vi.end(), 3) << endl;
}

Exercise 16.5

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
template <typename T1, unsigned N>
void print(const T1 (&array)[N])
{
  for(unsigned i = 0; i != N; ++i)
    cout << array[i] << " ";
}
int main()
{
  int a[3] = {1, 2, 3};
  print(a);
}

Exercise 16.6

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// begin function which accepts an array argument returns the pointer points to the first element of array;
// end function returns the pointer points to the tail off element of array.
template <typename T1, unsigned N>
const T1 *new_begin(const T1 (&array)[N])
{
  return array;
}
template <typename T1, unsigned N>
const T1 *new_end(const T1 (&array)[N])
{
  return new_begin(array) + N;
}
int main()
{
  int a[3] = {1, 2, 3};
  for(auto first = new_begin(a); first != new_end(a); ++first)
    cout << *first << endl;
}

Exercise 16.7

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
template <typename T, unsigned N>
constexpr unsigned number(const T (&array)[N])
{
  return N;
}
int main()
{
  int a[] = {1, 2, 3};
  cout << number(a) << endl;
}

Exercise 16.8

1
2
3
// Because overload '!=' operator is more widely implemented in C++ standard library class types than overload '<' operator;
// then when define a template which would compare two objects, code assumes that passed in arguments'type supports specified relationship operator, use '!=' operator is safer than '<'.

Exercise 16.9

1
2
3
4
5
6
7
// function template
// protype of function, could instantiate a specified version of function based on type and nontype arguments;
// class template
// protype of class, could instantiate a specified version of class based on type and nontype arguments;
// while compiler could not deduce the template parameter types for a class template;
// we must provide every additional information the template needs.

Exercise 16.10

1
2
3
4
// When a class template is instantiated:
// rewrite template, every instance of template type parameters would be replaced by given template type arguments, generate an individual class based on given template arguments;
// member functions of class template would be instantiated until they are called in program.

Exercise 16.11

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
template <typename elemType> class ListItem;
template <typename elemType> class List
{
public:
  List<elemType>();
  List<elemType>(const List<elemType> &);
  List<elemType>& operator=(const List<elemType> &);
  ~List();
  void insert(ListItem<elemType> *ptr, elemType value);
private:
  ListItem<elemType> *front, *end;
};

Exercise 16.12

1
2
// See Blob.h

Blob.h

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
template <typename T> class BlobPtr;
template <typename T> class Blob
{
  friend class BlobPtr<T>;
 public:
  using sh = My_shared_ptr<vector<T>>;
  typedef typename vector<T>::size_type size_type;
  Blob();
  Blob(initializer_list<T> il);
  template <typename It> Blob(It it1, It it2);
  size_type size() const {return data->size();}
  bool empty() const {return data->empty();}
  void push_back(const T &t) {data->push_back(t);}
  void push_back(T &&t) {data->push_back(std::move(t));}
  void pop_back();
  T &front();
  const T &front() const;
  T &back();
  const T &back() const;
  T &operator[](size_type i);
  BlobPtr<T> begin();
  BlobPtr<T> end();
 private:
  sh data;
  void check(size_type i, const string &msg) const;
};
template <typename T> Blob<T>::Blob(): data(sh()) {}
template <typename T> Blob<T>::Blob(initializer_list<T> il):
data(sh(il)) {}
template <typename T>
template <typename It>
Blob<T>::Blob(It it1, It it2): data(sh(it1, it2)) {}
template <typename T> void Blob<T>::check(size_type i, const string &msg) const
{
  if(i >= data->size())
    throw 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, "badk 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> T &Blob<T>::operator[](size_type i)
{
  check(i, "subscript out of range");
  return (*data)[i];
}
template <typename T> bool operator==(const BlobPtr<T> &a, const BlobPtr<T> &b)
{
  if(a.wptr.lock() == b.wptr.lock())
    {
      throw runtime_error("ptrs to different Blobs!");
    }
  return a.curr == b.curr;
}
template <typename T> bool operator< (const BlobPtr<T> &a, const BlobPtr<T> &b)
{
  if(a.wptr.lock() != rhs.wptr.lock())
    {
      throw runtime_error("ptrs to different Blobs!");
    }
  return a.curr < b.curr;
}
template <typename T> class BlobPtr
{
  friend bool operator==<T>(const BlobPtr<T> &, const BlobPtr<T> &);
  friend bool operator< <T>
    (const BlobPtr<T> &, const BlobPtr<T> &);
 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:
  My_shared_ptr<vector<T>> check(size_t,const string&) const;
  weak_ptr<vector<T>> wptr;
  size_t curr;
};
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, "increment past end of StrBlobPtr");
  return *this;
}
template <typename T> My_shared_ptr<vector<T>> BlobPtr<T>::check(size_t i, const string &msg) const
{
  auto ret = wptr.lock();
  if(!ret)
    throw runtime_error("unbound BlobPtr");
  if(i >= ret->size())
    throw out_of_range(msg);
  return ret;
}

Exercise 16.13

1
2
3
// See Blob.h.
// overload '==' operator function is a friend to BlobPtr class which is instantiated by the same type applied in the instantiation of this operator function.

Blob.h

Exercise 16.14

1
2
// See Screen.h.

Screen.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template <unsigned L, unsigned W> class Screen;
template <unsigned L, unsigned W> ostream &operator<<(ostream &os, Screen<L, W> s){
  os << "Length " << s.length
       << "Width " << s.width;
  return os;
}
template <unsigned L, unsigned W> istream &operator>>(istream &is, Screen<L, W> s){
  is >> s.length >> s.width;
  return is;
}
template <unsigned L, unsigned W>
class Screen
{
  // operator<< access private member of instance of this template
  // both class and function instantiated by same template nontype arguments.
  friend ostream &operator<<<L, W>(ostream &, Screen<L, W>);
  // operator>> access private member of instance of this template
  // both class and function instantiated by same template nontype arguments.
  friend istream &operator>><L, W>(istream &, Screen<L, W>);
  unsigned lenth = L;
  unsigned width = W;
};

Exercise 16.15

1
2
// See Screen.h.

Screen.h

Exercise 16.16

1
2
3
4
5
6
7
8
9
// See StrVec.h.
int main()
{
  Vec<int> vi1 = {1, 2, 3};
  Vec<int> vi2 = {1, 2, 4};
  cout << vi1[0] << endl;
  cout << (vi1 == vi2) << endl;
}

StrVec.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#ifndef STRVEC_H
#define STRVEC_H
class StrVec
{
  friend bool operator==(const StrVec &, const StrVec &);
  friend bool operator!=(const StrVec &, const StrVec &);
  friend bool operator<(const StrVec &, const StrVec &);
 public:
 StrVec(): elements(nullptr), first_free(nullptr), cap(nullptr) {}
  StrVec(const StrVec &);
  StrVec(StrVec &&) noexcept;
  StrVec(const initializer_list<string> &is);
  StrVec &operator=(const StrVec &);
  StrVec &operator=(StrVec &&) noexcept;
  StrVec &operator=(const initializer_list<string> &);
  string &operator[](size_t n);
  const string &operator[](size_t n) const;
  ~StrVec();
  void push_back(const string &);
  template <typename... Args> void emplace_back(Args&&...);
  size_t size() const {return first_free - elements;}
  size_t capacity() const {return cap - elements;}
  void reserve(size_t);
  void resize(size_t n, string s = "");
  string *begin() const {return elements;}
  string *end() const {return first_free;}
 private:
  allocator<string> alloc; /* it using static, 'undefined reference' reported when compile */
  void chk_n_alloc()
  {if(size() == capacity()) reallocate();}
  pair<string *, string *> alloc_n_copy
    (const string *, const string *);
  void free();
  void reallocate(size_t n = 0);
  string *elements;
  string *first_free;
  string *cap;
};
#endif

Exercise 16.17

1
2
3
// There is no difference between keyword typename and class;
// keyword typename would make template declaration and definition much straighter, for 'typename' could be referred as built-in type or class type, while 'class' is more likely regarded as class type.

Exercise 16.18

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// (a)
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> void f4(T, T);
// (e)
typedef char Ctype;
// template declaration below hides this typedef
template <typename Ctype> Ctype f5(Ctype a);

Exercise 16.19

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
template <typename T> ostream &print_container(const T &c)
{
  for(typename T::size_type i = 0; i != c.size(); ++i)
    cout << c[i] << " ";
  return cout;
}
int main()
{
  vector<int> vi = {1, 2, 3};
  print_container(vi) << endl;
}

Exercise 16.20

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
template <typename T> ostream &print_container(const T &c)
{
  for(typename T::const_iterator beg = c.begin(); beg != c.end(); ++beg)
    cout << *beg << " ";
  return cout;
}
int main()
{
  vector<int> vi = {1, 2, 3};
  print_container(vi) << endl;
}

Exercise 16.21

1
2
// See DebugDelete.h.

DebugDelete.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class DebugDelete
{
 public:
 DebugDelete(ostream &s = cerr): os(s) {}
  template <typename T> void operator()(T *p) const
    {os << "deleting unique_ptr" << endl; delete p;}
 private:
  ostream &os;
};

Exercise 16.22

1
2
// See TextQuery.h.

TextQuery.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#ifndef TEXTQUERY_H
#define TEXTQUERY_H
class QueryResult;
class TextQuery
{
  friend QueryResult;
  My_shared_ptr<vector<string>> sp1;
  My_shared_ptr<map<string, set<size_t>>> sp2;
 public:
  TextQuery(ifstream &infile);
  QueryResult query(const string &s);
};
TextQuery::TextQuery(ifstream &infile):
sp1(new vector<string>(), DebugDelete()), sp2(new map<string, set<size_t>>(), DebugDelete())
{
  size_t line_number = 0;
  string line;
  while(getline(infile, line))
    {
      // store line in vector<string>
      sp1->push_back(line);
      // scan through the line
      istringstream ist(line);
      string word;
      while(ist >> word)
      (*sp2)[word].insert(line_number);
      // increase the line_number
      ++line_number;
    }
}
class QueryResult
{
  size_t times = 0;
  string search_word;
  map<size_t, string> result;
  My_shared_ptr<vector<string>> sp1;
  My_shared_ptr<map<string, set<size_t>>> sp2;
 public:
  QueryResult(const string &s, TextQuery &t);
  size_t get_times() const {return times;}
  const map<size_t, string> &get_result() const {return result;}
};
QueryResult::QueryResult(const string &s, TextQuery &t):
sp1(t.sp1), sp2(t.sp2), search_word(s)
{
  set<size_t> line_numbers;
  auto iter = (*sp2).find(search_word);
  auto last = (*sp2).end();
  if(iter != last)
    {
      line_numbers = iter->second;
      for(const auto &n : line_numbers)
      {
	result[n] = (*sp1)[n];
	++times;
      }
    }
}
ostream &print(ostream &o, const QueryResult &q)
{
  o << "element occurs " << q.get_times() << " times" << endl;
  if(q.get_times())
    {
      auto result = q.get_result();
      for(const auto &p : result)
      cout << "\t(line " << p.first + 1 << ") "
	   << p.second << endl;
    }
  return o;
}
QueryResult TextQuery::query(const string &s)
{
  return QueryResult(s, *this);
}
#endif

Exercise 16.23

1
2
3
// When the use count of shared_ptr object decreases to 0, operator() member function of DebugDelete class type object would be called to release the resource allocated in dynamic memory.
// Like, when the query program reaches to the end of function runQueries and returns.

Exercise 16.24

1
2
// See Blob.h.

Blob.h

Exercise 16.25

1
2
3
extern template class std::vector<std::string>; // instantiation declaration, the definition of it is somewhere else
template class std::vector<Sales_data>;		// instantiation definition, the compiler will generate code fot it.

Exercise 16.26

1
2
3
4
5
6
// No;
// explicit instantiation of 'template <typename T> vector<T>' would instantiates every member of this class;
// for some constructors of class vector<T> would call class T's default constructor, this requiers that type T owns a default constructor;
// if there is no default constructor defined within class NoDefault, corresponding constructor as member function could not be instantiated;
// then this explicit instantiation failed.

Exercise 16.27

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
template <typename T> class Stack {};
void f1(Stack<char>);		// instantiation of Stack<char>, statement as 'Stack<char>' first appearance
class Exercise
{
  Stack<double> &rsd;		// instantiation of Stack<double>, its first appearance in context
  Stack<int> si;		// instantiation of Stack<int>, its first appearance int context
};
int main()
{
  Stack<char> *sc;		// no instantiation, class instantiated before;
  f1(*sc);			// no instantiated, just call function
  int iObj = sizeof(Stack<string>); // instantiation, its first appearance in context
}

Exercise 16.28

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// See My_shared_ptr.h and My_unique_ptr.h.
void runQueries(ifstream &infile)
{
  TextQuery tq(infile);
  string s;
  do
    {
      cout << "Enter word to look for, or q to quit: ";
      cin >> s;
      print(cout, tq.query(s)) << endl;
    }
  while(cin && s != "q");
}
int main(int argc, char *argv[])
{
  ifstream ifs(argv[1]);
  runQueries(ifs);
}

My_shared_ptr.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#ifndef MY_SHARED_PTR_H
#define MY_SHARED_PTR_H
template <typename T> class My_shared_ptr
{
 public:
  using del_type = function<void(T*)>;
 My_shared_ptr(): p(new T()), use_count(new unsigned(1)) {}
 My_shared_ptr(T *q): p(q), use_count(new unsigned(1)) {}
 My_shared_ptr(T *q, del_type d): p(q), del(d), use_count(new unsigned(1)) {}
 My_shared_ptr(const My_shared_ptr &m): p(m.p), del(m.del), use_count(m.use_count) {++*use_count;}
 My_shared_ptr(initializer_list<typename T::value_type> il): p(new T(il)), use_count(new unsigned(1)) {}
 My_shared_ptr(typename T::iterator it1, typename T::iterator it2):
  p(new T(it1, it2)), use_count(new unsigned(1)) {}
  My_shared_ptr &operator=(const My_shared_ptr &m);
  void reset();
  void reset(T *q);
  void reset(T *q, del_type d);
  T *operator->() const {return p;}
  T &operator*() const {return *p;}
  ~My_shared_ptr();
 private:
  T* p;
  unsigned *use_count;
  del_type del = [] (T *p) {delete p;};
};
template <typename T>
My_shared_ptr<T> &My_shared_ptr<T>::operator=(const My_shared_ptr &m)
{
  ++*m.use_count;
  if(--*use_count == 0)
    {
      del(p);
      delete use_count;
    }
  p = m.p;
  use_count = m.use_count;
  return *this;
}
  template<typename T> void My_shared_ptr<T>::reset()
  {
    if(--*use_count == 0)
      del(p);
    p = nullptr;
    *use_count = 0;
  }
template<typename T> void My_shared_ptr<T>::reset(T *q)
{
  if(--*use_count == 0)
    del(p);
  p = q;
  *use_count = 1;
}
template<typename T> void My_shared_ptr<T>::reset(T *q, del_type d)
{
  if(--*use_count == 0)
    del(p);
  p = q;
  del = d;
  *use_count = 1;
}
template<typename T> My_shared_ptr<T>::~My_shared_ptr()
{
  if(--*use_count == 0)
    {
      del(p);
      delete use_count;
    }
}
#endif

My_unique_ptr.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#ifndef MY_UNIQUE_PTR_H
#define MY_UNIQUE_PTR_H
template <typename T> class Delete
{
 public:
  void operator()(T *p)
  {
    delete p;
    cout << "class Delete" << endl;
  }
};
template <typename T, typename U = Delete<T>> class My_unique_ptr
  {
  public:
  My_unique_ptr(): p(new T()) {}
  My_unique_ptr(T *q): p(q) {}
  My_unique_ptr(T *q, U d): p(q), del(d) {}
  void reset() {del(p); p = nullptr;}
  void reset(T *q) {del(p); p = q;};
  ~My_unique_ptr() {del(p);}
  My_unique_ptr(const My_unique_ptr &m) = delete;
  My_unique_ptr &operator=(const My_unique_ptr &m) = delete;
  private:
  T *p;
  U del;
  };
#endif

Exercise 16.29

1
2
// See Blob.h.

Blob.h

Exercise 16.30

1
2
3
4
5
6
7
8
int main()
{
  Blob<int> b = {1, 2, 3};
  auto length = b.size();
  for(size_t index = 0; index != length; ++index)
    cout << b[index] << endl;
}

Exercise 16.31

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// As described in My_unique_ptr.h;
// if given a statement like this: My_unique_ptr<int, DebugDelete>
// code of destructor: ~My_unique_ptr() {del(p);}
// the destructor of this instantiated class would be expanded like:
// ~My_unique_ptr()
// {
//   cerr << "deleting unique_ptr" << endl;
//   delete p;
// }

Exercise 16.32

1
2
3
4
5
// During template argument deduction:
// as for function template, compiler deduces the template argument from the passed in function arguments, instantiates a version of the function best matches the given call;
// if one function paramenter uses template type parameter, only const conversion and array-pointer & function-pointer conversion could be applied to corresponding passed in arguments;
// usually compiler does not apply conversion to arguments, but instantiates a new template instance based on the type of arguments.

Exercise 16.33

1
2
3
// 1. const conversion
// 2. if the function parmeter is not a reference type, array argument converted to pointer which points to the its first element; function argument converted to pointer which points to this function.

Exercise 16.34

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
template <class T> int compare(const T &, const T &);
// a. compare("hi", "world");
// illegal;
// the function parameters have same type, and both are reference;
// and "hi" and "world" passed in as function arguments, the first's type is 'const char[3]', the second is 'const char[6]'
// T deduced from first function argument and second does not match.
// b. compare("bye", "dad");
// legal;
// same theory as mentioned in a:
// now T deduced from first function argument and second matches;
// T: char[3]

Exercise 16.35

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
template <typename T> T calc(T, int);
template <typename T> T fcn(T, T);
double d; float f; char c;
// a. calc(c, 'c');
// legal, T: char;
// b. calc(d, f);
// legal, T: double
// c. fcn(c, 'c');
// legal, T: char
// d. fcn(d, f);
// illegal, function template fcn's parameter list accepts two paramters with same type which is template type parameter, now for first time template type parameter deduced from function argument is double, second time float, not match.

Exercise 16.36

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
template <typename T> void f1(T, T);
template <typename T1, typename T2> void f2(T1, T2);
int i = 0, j = 42, *p1 = &i, *p2 = &j;
const int *cp1 = &i, *cp2 = &j;
// a. f1(p1, p2);
// T of first template deduced as int*, instantiates a template instance: f1(int*, int*);
// b. f2(p1, p2);
// T1 of second template deduced as int*, T2 as int*, new instance: f2(int*, int*);
// c. f1(cp1, cp2);
// T of first template deduced as const int*, nes instance: f1(const int*, const int*)
// d. f2(cp1, cp2);
// T1 of second template deduced as const int*, second as const int*, new instance: f2(const int*, const int*)
// e. f1(p1, cp2);
// illegal;
// T deduced from first argument of f1 is int*, deduced from seond argument is const int*, not match
// f. f2(p1, cp1);
// T1 of second template deduced as int*, T2 of second template deduced as const int*, new instance: f2(int*, const int*)

Exercise 16.37

1
2
3
4
5
6
// legal;
// just offer an explicit template argument;
// int a = 1;
// double b = 2.1;
// max<double>(a, b);

Exercise 16.38

1
2
3
4
// function template instance make_shared<T> accepts function arguments which could be passed in to type T's corresponding constructor to initialize a T type object allocated in dynamic memory;
// and compiler could not deduce T from function arguments of call of make_shared without specify any explicit template type argument;
// then a call like make_shared(args) is illegal.

Exercise 16.39

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
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()
{
  compare<std::string>("hello", "world");
}

Exercise 16.40

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
template <typename It>
auto fcn3(It beg, It end) -> decltype(*beg + 0)
{
  return *beg;
}
// legal;
// the type of passed in arguments of fcn3 must support '*' operator;
// and type of object of *beg should support '+' operator;
// return type is the type of element which is pointed by beg.

Exercise 16.41

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
template <typename T>
auto sum(T a, T b) -> decltype(a + b)
{
  return a + b;
}
int main()
{
  auto s = sum(123456789123456789123456789123456789123456789, 123456789123456789123456789123456789123456789);
  std::cout << s << std::endl;
}

Exercise 16.42

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
template <typename T> void g(T&& val);
int i = 0; const int ci = i;
// a. g(i);
// T: int&
// val: int&
// b. g(ci);
// T: const int&
// val: const int&
// c. g(i * ci);
// T: int
// val: int&&

Exercise 16.43

1
2
3
4
// template defined in 16.42.cpp
// g(i = ci)
// T: int&

Exercise 16.44

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// template <typename T> void g(T val);
// int i = 0; const int ci = i;
// a. g(i);
// T: int
// b. g(ci);
// T: const int;
// some one said here should be int and const is ignored.
// c. g(i * ci);
// T: int
// template <typename T> void g(const T& val);
// int i = 0; const int ci = i;
// a. g(i);
// T: int
// b. g(ci);
// T: int
// c. g(i * ci)
// T: int

Exercise 16.45

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
template <typename T> void g(T&& val) {vector<T> v;}
// call g with a literal constant of 42;
// T: int
// val: int&&
// initialize a empty vector with element type as int within instantiated function body.
// call g with a variable of type int;
// T: int&
// val: int
// vector<int&> is not legal.

Exercise 16.46

1
2
3
4
5
6
7
////////////////////////////////////////////////////
// for(size_t i = 0; i != size(); ++i)		  //
//   alloc.construct(dest++, std::move(*elem++)); //
////////////////////////////////////////////////////
// new string object constructed in new dynamic memory space takes over the resource owned by the original string which is allocated in another dynamic memory;
// with a for loop and the move constructor of string type, old string objects' resource is now owned by new constructed string objects, then the old could be safely destructed.

Exercise 16.47

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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 f1(int &t1, int &t2)
{
  cout << t1 << " " << t2 << endl;
}
void f2(int &&t1, int &&t2)
{
  cout << t1 << " " << t2 << endl;
}
int main()
{
  int i = 2, j = 4;
  flip(f1, i, j);
  flip(f2, 2, 4);
}

Exercise 16.48

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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 << " 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));
}

Exercise 16.49

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 i = 42, *p = &i;
const int ci = 0, *p2 = &ci;
// g(42);
// call the instance of the third template: void g(int);
// g(p);
// call the instance of the fourth template: void g(int*);
// g(ci);
// call the instance of the third template: void g(const int);
// g(p2);
// call the instance of the fourth template: void g(const int*);
// f(42);
// call the instance of the first template: void f(int);
// f(p);
// call the instance of the first template: void f(int*);
// f(ci);
// call the instance of the first template: void f(const int);
// f(p2);
// call the instance of the second template: void f(const int*);

Exercise 16.50

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
template <typename T> void f(T)
{
  cout << "f(T)" << endl;
}
template <typename T> void f(const T*)
{
  cout << "f(const T*)" << endl;
}
template <typename T> void g(T)
{
  cout << "g(T)" << endl;
}
template <typename T> void g(T*)
{
  cout << "g(T*)" << endl;
}
int main()
{
  int i = 42, *p = &i;
  const int ci = 0, *p2 = &ci;
  g(42);
  g(p);
  g(ci);
  g(p2);
  f(42);
  f(p);
  f(ci);
  f(p2);
}

Exercise 16.51

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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 = "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
}

Exercise 16.52

1
2
// See 16.51.cpp.

16.51.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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 = "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
}

Exercise 16.53

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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()
{
  print(cout, 3) << endl;
  print(cout, 3, "hello world") << endl;
  print(cout, 3, 3.14, "hello", string("world"), '!') << endl;
}

Exercise 16.54

1
2
// compile error.

Exercise 16.55

1
2
3
4
5
// At last, within the call of print(os, rest...) the function parameter packet contains no function parameter;
// the this call equals to the form as print(os);
// there is no match overload version of print in the scope;
// compile error.

Exercise 16.56

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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 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 << " 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... Args>
ostream &errorMsg(ostream &os, const Args&... rest)
{
  return print(os, debug_rep(rest)...);
}
int main()
{
  errorMsg(cout, "hello", string("world"), 42) << endl;
}

Exercise 16.57

1
2
3
4
5
6
7
// errorMsg version defined in section 16.4.2
// advantage: arbitrary type(supports '<<' operator), better flexibility;
// disadvantage: more complicated code;
// errorMsg version defined in section 6.2.6
// advantage: simple and easy to understand;
// disvantage: requires that types of elements contained in a initilaizer_list object must be same or could be converted to a same type.

Exercise 16.58

1
2
// See StrVec.h and Vec.h.

StrVec.h

Vec.h

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#ifndef VEC_H
#define VEC_H
template <typename T> class Vec;
template <typename T> bool operator==(const Vec<T> &, const Vec<T> &);
template <typename T> bool operator!=(const Vec<T> &, const Vec<T> &);
template <typename T> bool operator<(const Vec<T> &, const Vec<T> &);
template <typename T> class Vec
{
  friend bool operator==<T>(const Vec<T> &, const Vec<T> &);
  friend bool operator!=<T>(const Vec<T> &, const Vec<T> &);
  friend bool operator< <T>(const Vec<T> &, const Vec<T> &);
 public:
 Vec(): elements(nullptr), first_free(nullptr), cap(nullptr) {}
  Vec(const Vec &);
  Vec(Vec &&) noexcept;
  Vec(const initializer_list<T> &is);
  Vec &operator=(const Vec &);
  Vec &operator=(Vec &&) noexcept;
  Vec &operator=(const initializer_list<T> &);
  T &operator[](size_t n);
  const T &operator[](size_t n) const;
  ~Vec();
  void push_back(const T &);
  template <typename... Args> void emplace_back(Args&&...);
  size_t size() const {return first_free - elements;}
  size_t capacity() const {return cap - elements;}
  void reserve(size_t);
  void resize(size_t n, T s = "");
  T *begin() const {return elements;}
  T *end() const {return first_free;}
 private:
  allocator<T> alloc; /* it using static, 'undefined reference' reported when compile */
  void chk_n_alloc()
  {if(size() == capacity()) reallocate();}
  pair<T *, T *> alloc_n_copy
    (const T *, const T *);
  void free();
  void reallocate(size_t n = 0);
  T *elements;
  T *first_free;
  T *cap;
};
template <typename T> void Vec<T>::push_back(const T &s)
{
  chk_n_alloc();
  alloc.construct(first_free++, s);
}
template <typename T>
template <typename... Args>
inline
void Vec<T>::emplace_back(Args&&... args)
{
  chk_n_alloc();
  alloc.construct(first_free++, std::forward<Args>(args)...);
}
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()
{
  if(elements)
    {
      for(auto p = first_free; p != elements;)
      alloc.destroy(--p);
      alloc.deallocate(elements, cap - elements);
    }
  /* for_each(elements, first_free, [this] (T &s) {alloc.destroy(&s);}); */
  /* alloc.deallocate(elements, cap - elements); */
}
template<typename T> Vec<T>::Vec(const Vec &s)
{
  auto newdata = alloc_n_copy(s.begin(), s.end());
  elements = newdata.first;
  first_free = cap = newdata.second;
}
template<typename T> Vec<T>::Vec(Vec &&s) noexcept: first_free(s.first_free), elements(s.elements), cap(s.cap)
{
  s.first_free = nullptr;
  s.elements = nullptr;
  s.cap = nullptr;
}
template<typename T> Vec<T>::Vec(const initializer_list<T> &is)
{
  auto newdata = alloc_n_copy(is.begin(), is.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 &rhs)
{
  auto data = alloc_n_copy(rhs.begin(), rhs.end());
  free();
  elements = data.first;
  first_free = cap = data.second;
  return *this;
}
template<typename T> T &Vec<T>::operator[](size_t n)
{
  if(n >= size())
    throw runtime_error("Out of range");
  else
    return *(elements + n);
}
template<typename T> const T &Vec<T>::operator[](size_t n) const
{
  if(n >= size())
    throw runtime_error("Out of range");
  else
    return elements[n];
}
template<typename T> Vec<T> &Vec<T>::operator=(Vec &&s) noexcept
{
  if(&s != this)
    {
      free();
      first_free = s.first_free;
      elements = s.elements;
      cap = s.cap;
      s.first_free = s.elements = s.cap = nullptr;
    }
  return *this;
}
template<typename T> Vec<T> &Vec<T>::operator=(const initializer_list<T> &il)
{
  auto data = alloc_n_copy(il.begin(), il.end());
  free();
  elements = data.first;
  first_free = cap = data.second;
  return *this;
}
template<typename T> void Vec<T>::reallocate(size_t n)
{
  auto newcapacity = n ? n : (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>::reserve(size_t n)
{
  if(n > capacity())
    reallocate(n);
}
template<typename T> void Vec<T>::resize(size_t n, T s)
{
  if(n > size() && n <= capacity())
    {
      uninitialized_fill(first_free, elements + n, s);
      first_free = elements + n;
    }
  else if(n > size() && n > capacity())
    {
      reallocate();
      uninitialized_fill(first_free, elements + n, s);
      first_free = elements + n;
    }
  else if(n < size())
    {
      for(auto p = first_free; p != elements + n;)
      alloc.destroy(--p);
    }
}
template <typename T> bool operator==(const Vec<T> &s1, const Vec<T> &s2)
{
  vector<T> temp1(s1.begin(), s1.end());
  vector<T> temp2(s2.begin(), s2.end());
  return temp1 == temp2;
}
template <typename T> bool operator!=(const Vec<T> &s1, const Vec<T> &s2)
{
  return !(s1 == s2);
}
template <typename T> bool operator<(const Vec<T> &s1, const Vec<T> &s2)
{
  vector<T> temp1(s1.begin(), s1.end());
  vector<T> temp2(s2.begin(), s2.end());
  return temp1 < temp2;
}
#endif

Exercise 16.59

1
2
3
4
5
// string s;
// svec.emplace_back(s);
// for s is lvalue;
// would call string class type's copy constructor to copy construct a new string object allocated in dynamic memory.

Exercise 16.60

1
2
3
4
5
6
7
8
// function make_shared behaves almost like emplace_back illustrated in section 16.4.3;
// make_shared should be a variadic template function that forwards all arguments to underlying constructors that allocate and initializes an object in dynamic memroy and build a shared_ptr by wrapping the raw pointer.
template <typename T, typename... Args>
shared_ptr<T> my_make_shared(Args&&... args)
{
  return shared_ptr<T>(new T(std::forward<Args>(args)...));
}

Exercise 16.61

1
2
// See function template my_make_shared in 16.60.cpp.

16.60.cpp

1
2
3
4
5
6
7
8
// function make_shared behaves almost like emplace_back illustrated in section 16.4.3;
// make_shared should be a variadic template function that forwards all arguments to underlying constructors that allocate and initializes an object in dynamic memroy and build a shared_ptr by wrapping the raw pointer.
template <typename T, typename... Args>
shared_ptr<T> my_make_shared(Args&&... args)
{
  return shared_ptr<T>(new T(std::forward<Args>(args)...));
}

Exercise 16.62

1
2
3
4
5
6
7
8
9
int main()
{
  unordered_multiset<Sales_data> ums;
  ums.insert(Sales_data());
  ums.insert(Sales_data("1234", 23, 10.1));
  for(auto iter = ums.cbegin(); iter != ums.cend(); ++iter)
    cout << *iter << endl;
}

Exercise 16.63

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
template <typename T>
size_t count_in_vector(const vector<T> &vt, T i)
{
  return count(vt.begin(), vt.end(), i);
}
template <>
size_t count_in_vector(const vector<const char *> &vt, const char *i)
{
  size_t n;
  for(auto iter = vt.begin(); iter != vt.end(); ++iter)
    {
      if(strcmp(i, *iter) == 0)
      ++n;
    }
  return n;
}
int main()
{
  vector<double> vd = {1.1, 2.2, 3.3, 4.4};
  vector<int> vi = {1, 2, 3, 4, 5};
  vector<string> vs = {string("hello"), string("world")};
  cout << count_in_vector(vd, 2.2) << endl;
  cout << count_in_vector(vi, 2) << endl;
  cout << count_in_vector(vs, "hello") << endl;
  cout << "Exercise 16.64:" << endl;
  vector<const char *> vc = {"hello", "world"};
  cout << count_in_vector(vc, "world") << endl;
}

Exercise 16.64

1
2
// See 16.63.cpp.

16.63.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
template <typename T>
size_t count_in_vector(const vector<T> &vt, T i)
{
  return count(vt.begin(), vt.end(), i);
}
template <>
size_t count_in_vector(const vector<const char *> &vt, const char *i)
{
  size_t n;
  for(auto iter = vt.begin(); iter != vt.end(); ++iter)
    {
      if(strcmp(i, *iter) == 0)
      ++n;
    }
  return n;
}
int main()
{
  vector<double> vd = {1.1, 2.2, 3.3, 4.4};
  vector<int> vi = {1, 2, 3, 4, 5};
  vector<string> vs = {string("hello"), string("world")};
  cout << count_in_vector(vd, 2.2) << endl;
  cout << count_in_vector(vi, 2) << endl;
  cout << count_in_vector(vs, "hello") << endl;
  cout << "Exercise 16.64:" << endl;
  vector<const char *> vc = {"hello", "world"};
  cout << count_in_vector(vc, "world") << endl;
}

Exercise 16.65

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 << " null pointer";
  return ret.str();
}
string debug_rep(const string &s)
{
  return '"' + s + '"';
}
template <>
string debug_rep(char *p)
{
  return debug_rep(string(p));
}
template <>
string debug_rep(const char *p)
{
  return debug_rep(string(p));
}

Exercise 16.66

1
2
3
4
// overload version of debub_rep function:
// advantage:
// disadvantage: overloading changes the function match.

Exercise 16.67

1
2
3
// Template specilization would not influence the mathch process of function;
// for that template specilization is a instance of a template, not an overload of it.