Exercise 14.1

1
2
3
4
5
6
7
/* diff */
// 1. an overload operator function could be called directly.
// 2. an overload operator function must either be a member of class of have at least one parameter of class type.
// 3. A few built-in operators guarantee the order in which operands are evaluated. These overload versions of these operators do not preserve order of evaluation and/or shourt-circuit evaluation, it is usually a bad idea to overload them.
/* same */
// The logic pattern are identical, for example, the overload operator should return a reference if the related built-in operator returns a reference.

Exercise 14.2

1
2
3
4
5
6
7
// See Sales_data.h and Sales_data.cpp.
int main()
{
  Sales_data data("1234", 12, 78.1);
  cout << data;
}

Sales_data.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
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
Sales_data& Sales_data::combine(const Sales_data &rhs)
{
  units_sold += rhs.units_sold;
  revenue += rhs.revenue;
  return *this;
}
inline double Sales_data::avg_price() const
{
  if(units_sold)
    return revenue/units_sold;
  else
    return 0;
}
istream &read(istream &is, Sales_data &item)
{
  double price;
  is >> item.bookNo >> item.units_sold >> price;
  if(is)
    item.revenue = price * item.units_sold;
  else
    item = Sales_data();
  return is;
}
istream &operator>>(istream &i, Sales_data &data)
{
  double price;
  i >> data.bookNo >> data.units_sold >> price;
  if(i)
    data.revenue = price * data.units_sold;
  else
    data = Sales_data();
  return i;
}
ostream &print(ostream &os, const Sales_data &item)
{
  os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price();
  return os;
}
ostream &operator<<(ostream &o, const Sales_data &data)
{
  o << data.isbn() << " " << data.units_sold << " " << data.revenue << " " << data.avg_price();
  return o;
}
Sales_data add(const Sales_data &item1, const Sales_data &item2)
{
  Sales_data result = item1;
  return result.combine(item2);
}
Sales_data &Sales_data::operator+=(const Sales_data &data)
{
  return combine(data);
}
Sales_data &Sales_data::operator-=(const Sales_data &data)
{
  units_sold -= data.units_sold;
  revenue -= data.revenue;
  return *this;
}
Sales_data &Sales_data::operator=(const string &s)
{
  bookNo = s;
  units_sold = 0;
  revenue = 0.0;
  return *this;
}
Sales_data operator+(const Sales_data &data1, const Sales_data &data2)
{
  Sales_data temp = data1;
  temp += data2;
  return temp;
}
Sales_data operator-(const Sales_data &data1, const Sales_data &data2)
{
  Sales_data temp = data1;
  temp -= data2;
  return temp;
}
bool operator==(const Sales_data &data1, const Sales_data &data2)
{
  return data1.bookNo == data2.bookNo &&
    data1.units_sold == data2.units_sold &&
    data1.revenue == data2.revenue;
}
bool operator!=(const Sales_data &data1, const Sales_data &data2)
{
  return !(data1 == data2);
}

Sales_data.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
#ifndef SALES_DATA_H
#define SALES_DATA_H
struct Sales_data;
istream &read(istream &is, Sales_data &item);
ostream &print(ostream &os, const Sales_data &item);
Sales_data add(const Sales_data &item1, const Sales_data &item2);
struct Sales_data {
  friend istream &read(istream &, Sales_data &);
  friend ostream &print(ostream &, const Sales_data &);
  friend Sales_data add(const Sales_data &item1, const Sales_data &item2);
  friend Sales_data operator+(const Sales_data &, const Sales_data &);
  friend istream &operator>>(istream &, Sales_data &);
  friend ostream &operator<<(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 operator-(const Sales_data &, const Sales_data &);
public:
  // Sales_data(): bookNo(""), units_sold(0), revenue(0) {}
  // Exercise 7.14 above has misleading due to the CN version.
  // It's ok to use the in-class initializer values.
Sales_data(): Sales_data("", 0, 0)
  {cout << " Default";};
Sales_data(const string &s): Sales_data(s, 0, 0)
  {cout << " With string argument";}
Sales_data(const string &s, unsigned n, double p):
  bookNo(s), units_sold(n), revenue(p*n)
  {cout << " With three arguments";}
Sales_data(istream &is): Sales_data()
  {cout << " With istream argument"; read(is, *this);}
  string isbn() const {return bookNo;}
  Sales_data& combine(const Sales_data&);
  Sales_data &operator+=(const Sales_data &);
  Sales_data &operator-=(const Sales_data &);
  Sales_data &operator=(const string &);
  operator string() const {return bookNo;}
  operator double() const {return revenue;}
private:
  double avg_price() const;
  string bookNo;
  unsigned units_sold = 0;
  double revenue = 0.0;
};
#endif

Exercise 14.3

1
2
3
4
5
// a "cobble" == "stone"; neither(unspecified behaviour, use strcmp instead)
// b svec1[0] == svec2[0]; overload == operator of string
// c svec1 == svec2; overload == operator of vector
// d svec1[0] == "stone"; overload == operator of string

Exercise 14.4

1
2
3
4
5
6
7
8
9
// a %; not a member of class type, 1st: does not modify the operands, 2nd: operands may need convertion;
// b %=; should ba a member of class type, 1st: would modiry the left operand;
// c ++; member, same reason as b;
// d ->; member, -> operator must be a member of class type.
// e <<: shoud not be member, because left operand is ostream object, even though there is a overload << operator defined within class type as member, the expression would not call it.
// f &&: not a member, same reason as a;
// g ==: not a member, same reason as a;
// f (): member, () operator must be a member of class type.

Exercise 14.5

1
2
// See Employee.h.

Employee.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
#ifndef EMPLOYEE_H
#define EMPLOYEE_H
class Employee;
istream &read(istream &is, Employee &e);
class Employee
{
  friend istream &read(istream &is, Employee &e);
  friend istream &operator>>(istream &is, Employee &e);
  friend ostream &operator<<(ostream &os, const Employee &e);
  friend bool operator==(const Employee &e1, const Employee &e2);
  friend bool operator!=(const Employee &e1, const Employee &e2);
  using str = string;
  str name;
  str address;
  str contact;
  str department;
  str job;
 public:
 Employee(): Employee("") {};
 Employee(str n, str a = "", str c = "", str d = "", str j = ""):
  name(n), address(a), contact(c),  department(d), job(j) {}
  // Exercise 7.40: An Employee object better has a name, left could be filled later
  // And compiler could be able to distinguish above two constructors
 Employee(istream &is): Employee() {is >> *this;}
  Employee &operator=(const Employee &);
  Employee &operator=(Employee &&) noexcept;
  Employee &operator=(const string &);
  explicit operator bool() const
  {return name.size();}
};
#endif

Exercise 14.6

1
2
// See Sales_data.h and Sales_data.cpp.

Sales_data.cpp

Sales_data.h

Exercise 14.7

1
2
// See String.h.

String.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
#ifndef STRING_H_EX
#define STRING_H_EX
class String
{
  friend bool operator==(const String &, const String &);
  friend bool operator!=(const String &, const String &);
  friend bool operator<(const String &, const String &);
  friend ostream& operator<<(ostream &, const String&);
 public:
 String(): elements(nullptr), first_free(nullptr), cap(nullptr) {}
  String(const char *);
  String(const String &);
  String(String &&) noexcept;
  String &operator=(const String &);
  String &operator=(String &&) noexcept;
  char &operator[](size_t n);
  const char &operator[](size_t n) const;
  ~String();
  void push_back(const char &);
  size_t size() const {return first_free - elements;}
  size_t capacity() const {return cap - elements;}
  char *begin() const {return elements;}
  char *end() const {return first_free;}
  ostream &print();
 private:
  allocator<char> alloc; /* it using static, 'undefined reference' reported when compile */
  void chk_n_alloc()
  {if(size() == capacity()) reallocate();}
  pair<char *, char *> alloc_n_copy
    (const char *, const char *);
  void free();
  void reallocate(size_t n = 0);
  char *elements;
  char *first_free;
  char *cap;
};
#endif

Exercise 14.8

1
2
// See Employee.h

Employee.h

Exercise 14.9

1
2
// See Sales_data.h and Sales_data.cpp.

Sales_data.cpp

Sales_data.h

Exercise 14.10

1
2
3
4
5
6
7
8
9
// a, 0-201-99999-9 10 24.95 generates a valid Sales_data object with right data members.
// illegal input, the interger part of 24.95 would be stored in data member units_sold, then left part .95 would be treated as third input and converted to a float stored in this object as data member price; finally the data of this object contains is false.
int main()
{
  Sales_data a;
  cin >> a;
  cout << a << endl;
}

Exercise 14.11

1
2
3
4
// This overload >> verseion illustrated in text is exactly the most original version I implemented before.
// Of course bug exists.
// It does not check the state of the istream object, the final Sales_data object modified throuth istream may be unexpected, for the data members of which may not be associated as one valid Sales_data object.

Exercise 14.12

1
2
// See Employee.h.

Employee.h

Exercise 14.13

1
2

Exercise 14.14

1
2
3
// refraction friendly;
// simplified code;

Exercise 14.15

1
2
3
// No;
// for the Employee class type defined before, to overload arithmetic operators for it is meaningless, due to that calculating the sum or something else of Employee objects is pointless.

Exercise 14.16

1
2
// See StrBlob.h, StrVec.h, String.h.

String.h

StrBlob.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 STRBLOB_H
#define STRBLOB_H
class StrBlobPtr;
class StrBlob
{
  friend class StrBlobPtr;
  friend class ConstStrBlobPtr;
  friend bool operator==(const StrBlob &, const StrBlob &);
  friend bool operator!=(const StrBlob &, const StrBlob &);
  friend bool operator<(const StrBlob &, const StrBlob &);
 public:
  typedef vector<string>::size_type size_type;
  StrBlob();
  StrBlob(initializer_list<string> il);
  StrBlob(const StrBlob &);
  StrBlob &operator=(const StrBlob &);
  string &operator[](size_t n);
  const string &operator[](size_t n) const;
  size_type size() const {return data->size();}
  bool empty() const {return data->empty();}
  void push_back(const string &t) {data->push_back(t);}
  void push_back(string &&t) {data->push_back(std::move(t));}
  void pop_back();
  string &front();
  const string &front() const;
  string &back();
  const string &back() const;
  StrBlobPtr begin();
    StrBlobPtr end();
 private:
  shared_ptr<vector<string>> data;
  void check(size_type i, const string &msg) const;
};
class StrBlobPtr
{
  friend bool operator==(const StrBlobPtr &, const StrBlobPtr &);
  friend bool operator!=(const StrBlobPtr &, const StrBlobPtr &);
  friend bool operator<(const StrBlobPtr &, const StrBlobPtr &);
  friend StrBlobPtr operator+(const StrBlobPtr &, size_t n);
  friend StrBlobPtr operator-(const StrBlobPtr &, size_t n);
 public:
  string &operator[](size_t n);
  const string &operator[](size_t n) const;
  StrBlobPtr &operator++();
  StrBlobPtr &operator--();
  StrBlobPtr operator++(int);
  StrBlobPtr operator--(int);
  string &operator*() const;
  string *operator->() const;
 StrBlobPtr(): curr(0) {}
 StrBlobPtr(StrBlob &a, size_t sz = 0):
  wptr(a.data), curr(sz) {}
  string &deref() const;
  StrBlobPtr &incr();
 private:
  shared_ptr<vector<string>> check(size_t,const string&) const;
  weak_ptr<vector<string>> wptr;
  size_t curr;
};
class ConstStrBlobPtr
{
 public:
 ConstStrBlobPtr(): curr(0) {}
 ConstStrBlobPtr(const StrBlob &a, size_t sz = 0):
  wptr(a.data), curr(sz) {}
  const string &deref() const;
  ConstStrBlobPtr &incr();
  const string &operator*();
  const string *operator->();
 private:
  shared_ptr<vector<string>> check(size_t, const string&) const;
  weak_ptr<vector<string>> wptr;
  size_t curr;
};
#endif

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
#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 &);
  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 14.17

1
2
3
// Yes;
// See Employee.h.

Employee.h

Exercise 14.18

1
2
// See StrBlob.h, StrVec.h and String.h.

StrBlob.h

StrVec.h

String.h

Exercise 14.19

1
2
3
4
5
// No;
// overload relationship operators of it, like operator <, is not logic reliable;
// because there are several data members contained in Employee class type, relationship could be concluded based on name, address or job, etc;
// there would be conflict between relationship operators and == operator.

Exercise 14.20

1
2
// See Sales_data.h and Sales_data.cpp.

Sales_data.cpp

Sales_data.h

Exercise 14.21

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//////////////////////////////////////////////////////////////////////
// Sales_data operator+(const Sales_data &d1, const Sales_data &d2) //
// {								    //
//   Sales_data temp(d1);					    //
//   temp.units_sold += d2.units_sold;				    //
//   temp.revenue += d2.revenue;				    //
//   return temp;						    //
// }								    //
// 								    //
// Sales_data &Sales_data::operator+=(const Sales_data &d)	    //
// {								    //
//   *this = *this + d;						    //
//   return *this;						    //
// }								    //
//////////////////////////////////////////////////////////////////////
// disadvantages:
// redundant work compared to the original version defined in section 14.3 and 14.4, for example, the unnecessary copy-assignment operation within overload operator +=.

Exercise 14.22

1
2
// See Sales_data.h and Sales_data.cpp.

Sales_data.cpp

Sales_data.h

Exercise 14.23

1
2
// See StrVec.h.

StrVec.h

Exercise 14.24

1
2
3
// Yes;
// See Employee.h.

Employee.h

Exercise 14.25

1
2
3
// Needs another overload assignment operator which accepts one const string & parameter, this parameter would be used to assign data member 'name';
// the right operand should be string object, or object which could be implicitly converted to string object, for the data member 'name' is a string type object.

Exercise 14.27

1
2
// Seee StrBlob.h and StrBlob.cpp.

StrBlob.h

StrBlob.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
 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
189
190
191
// StrBlob member
StrBlob::StrBlob(): data(make_shared<vector<string>>()) {}
StrBlob::StrBlob(initializer_list<string> il):
data(make_shared<vector<string>>(il)) {}
StrBlob::StrBlob(const StrBlob &s): data(make_shared<vector<string>>(*s.data)) {}
StrBlob &StrBlob::operator=(const StrBlob &s)
{
  data = make_shared<vector<string>>(*s.data);
  return *this;
}
inline string &StrBlob::operator[](size_t n)
{
  return data->at(n);
}
inline const string &StrBlob::operator[](size_t n) const
{
  return data->at(n);
}
void StrBlob::check(size_type i, const string &msg) const
{
  if(i >= data->size())
    throw out_of_range(msg);
}
string &StrBlob::front()
{
  check(0, "front on empty StrBlob");
  return data->front();
}
const string &StrBlob::front() const
{
  check(0, "front on empty StrBlob");
  return data->front();
}
string &StrBlob::back()
{
  check(0, "badk on empty StrBlob");
  return data->back();
}
const string &StrBlob::back() const
{
  check(0, "back on empty StrBlob");
  return data->back();
}
bool operator==(const StrBlob &s1, const StrBlob &s2)
{
  return *(s1.data) == *(s2.data);
}
bool operator!=(const StrBlob &s1, const StrBlob &s2)
{
  return !(s1 == s2);
}
bool operator<(const StrBlob &s1, const StrBlob &s2)
{
  return *(s1.data) < *(s2.data);
}
// StrBlobPtr member
shared_ptr<vector<string>> StrBlobPtr::check(size_t i, const string &msg) const
{
  auto ret = wptr.lock();
  if(!ret)
    throw runtime_error("unbound StrBlobPtr");
  if(i >= ret->size())
    throw out_of_range(msg);
  return ret;
}
string &StrBlobPtr::operator[](size_t n)
{
  auto p = check(n, "out of range");
  return (*p)[n];
}
const string &StrBlobPtr::operator[](size_t n) const
{
  auto p = check(n, "out of range");
  return (*p)[n];
}
StrBlobPtr &StrBlobPtr::operator++()
{
  check(curr, "increment past end of StrBlobPtr");
  ++curr;
  return *this;
}
StrBlobPtr &StrBlobPtr::operator--()
{
  --curr;
  check(curr, "increment past end of StrBlobPtr");
  return *this;
}
StrBlobPtr StrBlobPtr::operator++(int)
{
  StrBlobPtr ret(*this);
  ++*this;
  return ret;
}
StrBlobPtr StrBlobPtr::operator--(int)
{
  StrBlobPtr ret(*this);
  --*this;
  return ret;
}
string &StrBlobPtr::operator*() const
{
  auto p = check(curr, "dereference pass end");
  return (*p)[curr];
}
string *StrBlobPtr::operator->() const
{
  return &this->operator*();
}
string &StrBlobPtr::deref() const
{
  auto p = check(curr, "deference pass end");
  return (*p)[curr];
}
StrBlobPtr &StrBlobPtr::incr()
{
  check(curr, "increment pass end of StrBlobPtr");
  ++curr;
  return *this;
}
bool operator==(const StrBlobPtr &s1, const StrBlobPtr &s2)
{
  return s1.curr == s2.curr;	// both StrBlobPtr object points to the same StrBlob object.
}
bool operator!=(const StrBlobPtr &s1, const StrBlobPtr &s2)
{
  return !(s1 == s2);
}
bool operator<(const StrBlobPtr &s1, const StrBlobPtr &s2)
{
  return s1.curr < s2.curr;
}
StrBlobPtr operator+(const StrBlobPtr &s1, size_t n)
{
  StrBlobPtr ret(s1);
  while(n)
    {
      ++ret;
      --n;
    }
  return ret;
}
StrBlobPtr operator-(const StrBlobPtr &s1, size_t n)
{
  StrBlobPtr ret(s1);
  while(n)
    {
      --ret;
      --n;
    }
  return ret;
}
// ConstStrBlobPtr member
shared_ptr<vector<string>> ConstStrBlobPtr::check(size_t i, const string &msg) const
{
  auto ret = wptr.lock();
  if(!ret)
    throw runtime_error("unbound StrBlobPtr");
  if(i >= ret->size())
    throw out_of_range(msg);
  return ret;
}
const string &ConstStrBlobPtr::deref() const
{
  auto p = check(curr, "dereference pass end");
  return (*p)[curr];
}
ConstStrBlobPtr &ConstStrBlobPtr::incr()
{
  check(curr, "increment pass end of StrBlobPtr");
  ++curr;
  return *this;
}
const string &ConstStrBlobPtr::operator*()
{
  auto p = check(curr, "deference past end");
  return (*p)[curr];
}
const string *ConstStrBlobPtr::operator->()
{
  return &this->operator*();
}
StrBlobPtr StrBlob::begin()
{
  return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end()
{
  auto ret = StrBlobPtr(*this, data->size());
    return ret;
}

Exercise 14.28

1
2
// See StrBlob.h and StrBlob.cpp.

StrBlob.cpp

StrBlob.h

Exercise 14.29

1
2
3
// The overload ++ & -- operators as member functions could not be defined as const member functions,
// because within these functions'body, the caller StrBlobPtr object would be modified, and const object could not be modified.

Exercise 14.30

1
2
// See StrBlob.h and StrBlob.cpp.

StrBlob.cpp

StrBlob.h

Exercise 14.31

1
2
3
// StrBlobPtr behaves like iterator, the most convenient way to get a StrBlobPtr object is to call the begin or end member function of StrBlob object, this way is not only convenient but also safe and clean.
// the synthesized copy constructor, copy-assignment operator and destructor already satisfy our needs, define spedified copy constructor or copy-assignment operator would introduce uncertanity and complexity.

Exercise 14.32

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class StrBlobPtr_Ptr
{
  StrBlobPtr *ptr = nullptr;
public:
  StrBlobPtr_Ptr() = default;
  StrBlobPtr_Ptr(StrBlobPtr *p): ptr(p) {}
  StrBlobPtr operator->();
  // maybe StrBlobPtr *operator->(); ?
};
StrBlobPtr StrBlobPtr_Ptr::operator->()
{
  return *ptr;
}
int main()
{
  StrBlob sb{"hello", "world"};
  StrBlobPtr iter = sb.begin();
  StrBlobPtr_Ptr p(&iter);
  std::cout << p->size() << std::endl; // finally calls size() member function of string class type.
}

Exercise 14.33

1
2
// overload () operator could accept arbitrary(in fact, the actual amount is around 256 settled by compiler) operands, the implicit pointer of caller object and rest passed in arguments.

Exercise 14.34

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
struct If_Then_Else
{
  int operator()(int, int, int) const;
};
int If_Then_Else::operator()(int a, int b, int c) const
{
  if(a)
    return b;
  else
    return c;
}

Exercise 14.35

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class ReadString
{
  istream &is;
public:
  ReadString(istream &i = cin): is(i) {};
  string operator()() const;
};

string ReadString::operator()() const
{
  string result;
  getline(is, result);
  if(is)
    return result;
  else
    return "";
}

Exercise 14.36

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int main()
{
  vector<string> vs;
  ReadString rs;
  string result;
  while((result = rs()).size())
    vs.push_back(result);
  for(const auto &i : vs)
    cout << i << endl;
}

Exercise 14.37

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Equal
{
  int base = 0;
public:
  Equal() = default;
  Equal(int i): base(i) {};
  bool operator()(int i);
};
bool Equal::operator()(int i)
{
  return i == base;
}
int main()
{
  Equal eq(3);
  vector<int> vi = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  replace_if(vi.begin(), vi.end(), eq, 4);
  for(const auto &i : vi)
    cout << i << endl;
}

Exercise 14.38

 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
class EqLength
{
  size_t length = 0;
public:
  EqLength() = default;
  EqLength(size_t n): length(n) {}
  bool operator()(const string &) const;
};
bool EqLength::operator()(const string &s) const
{
  return s.size() == length;
}
int main(int argc, char *argv[])
{
  if(argc != 2)
    cerr << "False command-line arguments, expects "
       << argc << endl;
  ifstream ifs(argv[1]);
  istream_iterator<string> beg(ifs);
  istream_iterator<string> eof;
  for(size_t l = 1; l < 11; ++l)
    {
      cout << "Length " << l
	 << " " << count_if(beg, eof, EqLength(l)) << endl;
    }
}

Exercise 14.39

 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
class EqLength
{
  size_t length = 0;
public:
  EqLength() = default;
  EqLength(size_t n): length(n) {}
  bool operator()(const string &) const;
};
bool EqLength::operator()(const string &s) const
{
  return s.size() == length;
}
class GeLength
{
  size_t length = 0;
public:
  GeLength() = default;
  GeLength(size_t n): length(n) {}
  bool operator()(const string &) const;
};
bool GeLength::operator()(const string &s) const
{
  return s.size() >= length;
}
int main(int argc, char *argv[])
{
  if(argc != 2)
    cerr << "False command-line arguments, expects "
       << argc << endl;
  ifstream ifs(argv[1]);
  istream_iterator<string> beg(ifs);
  istream_iterator<string> eof;
  for(size_t l = 1; l < 10; ++l)
    {
      cout << "Length " << l
	 << " " << count_if(beg, eof, EqLength(l)) << endl;
    }
  cout << "Length not smaller than 10 " << count_if(beg, eof, GeLength(10)) << endl;
}

Exercise 14.40

 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
class SizeSmaller
{
public:
  bool operator()(const string &w1, const string &w2) const
  {return w1.size() < w2.size();}
};
class SizeGE
{
  size_t sz;
public:
  SizeGE(size_t n): sz(n) {}
  bool operator()(const string &word) const
  {return word.size() >= sz;}
};
class PrintString
{
  ostream &o;
  char sep;
public:
  PrintString(ostream &os = cout, char c = ' '): o(os), sep(c) {}
  void operator()(const string &s) const
  {o << s << sep;}
};
void elimDups(vector<string> &words)
{
  sort(words.begin(), words.end());
  auto end_unique = unique(words.begin(), words.end());
  words.erase(end_unique, words.end());
}
void biggies(vector<string> &words, vector<string>::size_type sz)
{
  elimDups(words);
  stable_sort(words.begin(), words.end(), SizeSmaller());
  auto iter = partition(words.begin(), words.end(), SizeGE(sz));
  auto count = iter - words.begin();
  cout << count << " words" << endl;
  for_each(words.begin(), iter, PrintString(cout, '\n'));
}
int main()
{
  vector<string> vs =
    {"the", "time", "is", "near", "near", "the", "sunrise"};
  biggies(vs, 4);
}

Exercise 14.41

1
2
3
4
// lambda as a new feature introduced in new C++11 standard could make code easier to write, understand and debug;
// lambda expression would be applied if the predicate is one-time-use and not complicated;
// else function object is better.

Exercise 14.42

1
2
3
4
5
6
7
int main()
{
  auto greater1024 = bind(greater<int>(), _1, 1024);
  auto not_equal_to_pooh = bind(not_equal_to<string>(), _1, "pooh");
  auto multiplies_2 = bind(multiplies<int>(), _1, 2);
}

Exercise 14.43

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
int main()
{
  int i;
  vector<int> vi;
  cout << "Please input integers: " << endl;
  while(cin >> i)
    vi.push_back(i);
  cin.clear();
  int j;
  cout << "Please input a interger to test: " << endl;
  cin >> j;
  auto modulus_left = bind(modulus<int>(), j, _1);
  auto iter = find_if(vi.cbegin(), vi.cend(), modulus_left);
  if(iter == vi.cend())
    cout << "ok" << endl;
  else
    cout << "bad" << endl;
}

Exercise 14.44

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
int add(int i, int j) {return i + j;}
auto mod = [](int i, int j) {return i % j;};
struct divide
{
  int operator()(int denominator, int divisor)
  {return denominator / divisor;}
};
map<string, function<int(int, int)>> binops =
  {
    {"+", add},
    {"-", std::minus<int>()},
    {"/", divide()},
    {"*", [](int i, int j) {return i * j;}},
    {"%", mod}
  };
int main()
{
  cout << binops["-"](3, 4) << endl;
}

Exercise 14.45

1
2
// See Sales_data.h.

Sales_data.h

Exercise 14.46

1
2
3
4
// We should define the conversion operators illustrated in exercise 14.45 for Sales_data class type;
// and the first conversion operators should be explicit, for when there is a expression includes operands of Sales_data object and string object, due to that Sales_data class type has a constructor accepts one const string reference, causes amgibuous situation then this expression is illegal;
// the second one does not need to be explicit, there's no such concern for it as the first one.

Exercise 14.47

1
2
3
4
5
6
struct Integral
{
  operator const int();		// meaningless, for onr conversion operator should not define return type, this 'const' here is unspecified, it will be ignored by compiler.
  operator int() const;		// Not only non-const but also const Integral objects could call this one, Integral objects would be converted to int.
};

Exercise 14.48

1
2
3
4
// See Employee.h.
// class type Employee should define its bool conversion operator, for one Employee object may has no content if it is default constructed(or its data member 'name' is an empty string), and such object used as condition should be converted to bool;
// the bool conversion operator should be explicit, for it is only used in condition case(compiler would implicitly call this explicit conversion operator when encountered in condition expression), and it should not be implicitly called in other cases.

Employee.h

Exercise 14.49

1
2
// See Employee.h.

Employee.h

Exercise 14.50

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
struct LongDouble
{
  LongDouble(double = 0.0);
  operator double();
  operator float();
};
LongDouble ldObj;
int ex1 = ldObj;		// illegal
// could call ldObj.double() and then convert double to int;
// could call ldObj.float() and then convert float to int;
// both are at same level, this expression introduces ambiguous call.
float ex2 = ldObj;		// legal
// call ldObj.float() and no more built-in type conversion
// best match.

Exercise 14.51

1
2
3
4
5
6
7
8
9
// void calc(int);
// void calc(LongDouble);		// class LongDouble defined in 14.50
// double dval;
// calc(dval);			// finally call 'void calc(int)'
// double object dval converted to int type: built-in type conversion;
// double object dval converted to LongDouble class type: class type conversion
// for built-in arithmetical type conversion has higher priority than class type conversion;
// then the first calc one would be called.

Exercise 14.52

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// LongDouble operator+(LongDouble &, double);
// SamllInt si;
// LongDouble ld;
// ld = si + ld;
// candidates:
// 1. LongDouble operator+(LongDouble &, double);
// 2. member function: SmallInt operator+(const SmallInt &, const SmallInt &);
// 3. built-in + operator; ambiguous.
// none of these matchs.
// ld = ld + si;
// candidates:
// 1. LongDouble operator+(LongDouble &, double); si converted to int by class type conversion, and then int converted to double by built-in type conversion.
// 2. member function: LongDouble operator+(const SmallInt &); si const conversion.
// 3. built-in + operator; ambiguous
// candidate 2 is applied.

Exercise 14.53

1
2
3
4
5
6
7
8
9
// Based on class type SmallInt defined in section 14.9.3;
// SmallInt sl;
// double d = s1 + 3.14;
// illegal, ambiguous calling;
// 1. member function: SmallInt operator+(const SmallInt &, const SmallInt &);
// 2. built-in + operator;
// fix:
// double d = static_cast<int>(s1) + 3.14;