Exercise 12.1

1
2
3
// b1 has 4 elements;
// b2's code block has ended, os b2 is destroyed, there is no point in saying how many elements in b2.

Exercise 12.2

1
2
// See StrBlob.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
 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
class StrBlobPtr;
class StrBlob
{
  friend class StrBlobPtr;
  friend class ConstStrBlobPtr;
 public:
  typedef vector<string>::size_type size_type;
  StrBlob();
  StrBlob(initializer_list<string> il);
  size_type size() const {return data->size();}
  bool empty() const {return data->empty();}
  void push_back(const string &t) {data->push_back(t);}
  void pop_back();
  string &front();
  const string &front() const;
  string &back();
  const string &back() const;
  StrBlobPtr begin();
    StrBlobPtr end();
 private:
  shared_ptr<vector<std::string>> data;
  void check(size_type i, const string &msg) const;
};
StrBlob::StrBlob(): data(make_shared<vector<string>>()) {}
StrBlob::StrBlob(initializer_list<string> il):
data(make_shared<vector<string>>(il)) {}
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();
}
class StrBlobPtr
{
 public:
 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;
};
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::deref() const
{
  auto p = check(curr, "dereference pass end");
  return (*p)[curr];
}
StrBlobPtr &StrBlobPtr::incr()
{
  check(curr, "increment pass end of StrBlobPtr");
  ++curr;
  return *this;
}
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();
 private:
  shared_ptr<vector<string>> check(size_t,const string&) const;
  weak_ptr<vector<string>> wptr;
  size_t curr;
};
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;
}
StrBlobPtr StrBlob::begin()
{
  return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end()
{
  auto ret = StrBlobPtr(*this, data->size());
    return ret;
}

Exercise 12.3

1
2
3
4
// Unnecessary
// const member function version of front and back could not modify the shared_ptr data, but still able to modify the vector<string> which is pointed by data;
// there is slight difference between the const version and the normal version.

Exercise 12.4

1
2
3
// Because the parameter i is of size_type type, which is vector::size_type, it is unsigned.
// When passed i is a negative number, it would be transformmed to a really large unsigned integer, the comparison between it and data->size() would always turns true if the size is not so large.

Exercise 12.5

1
2
3
4
// The constructor function StrBlob::StrBlob(initializer_list<string> il) is not explicit;
// advantage: elements surrounded by curly braces could be implicitly transformmed to initializer_list object, for example, no need to explicitly construct a initialize_list<string> object to pass it to the constructor function; this feature allows copy initialization with different types.
// disadvantage: the explicit way is more strict, for implicit transformer may pass in an invalid argument which does not meet the requirement of parameter, introduces an error by carelessness; this feature only allows direct initialization.

Exercise 12.6

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
vector<int>* new_vector()
{
  return new vector<int>;
}
void read_vector(vector<int> *vi)
{
  int i;
  while(cin >> i)
    vi->push_back(i);
}
void print_vector(vector<int> *vi)
{
  for(const auto &i : *vi)
    cout << i << endl;
}
int main()
{
  auto p = new_vector();
  read_vector(p);
  print_vector(p);
  delete p;
}

Exercise 12.7

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
shared_ptr<vector<int>> new_vector()
{
  return make_shared<vector<int>>();
}
void read_vector(shared_ptr<vector<int>> p)
{
  int i;
  while(cin >> i)
    p->push_back(i);
}
void print_vector(shared_ptr<vector<int>> p)
{
  for(const auto &i : *p)
    cout << i << endl;
}
int main()
{
  auto p = new_vector();
  read_vector(p);
  print_vector(p);
}

Exercise 12.8

1
2
3
4
// illegal
// built-in type object defined inside code block but without value initialization, its value would be undefined;
// and p will convert to a bool, the dynamic memory allocated has no chance to be freed.

Exercise 12.9

1
2
3
// the memory space pointed by pointer r stays unchanged, like pointer q, both memory spaces are not freed.
// reference count of the object which was pointed by r2 became 0, so it is freed; on the contrast, reference count of another object increases to 2, it stays unchanged.

Exercise 12.10

1
2
// That is right.

Exercise 12.11

1
2
3
4
5
6
// shared_ptr<int> p(new int(42));
// process(shared_ptr<int>(p.get));
// when the call of precess ends, the variable ptr defined inside function body is deleted, and the memory space which is pointed by ptr is freed, too.
// though pointed to same memory space, the two shared_ptr objects are created individually, their reference count are both 1.
// after this, using shared_ptr p will cause undefined behaviour.

Exercise 12.12

1
2
3
4
5
6
7
// auto p = new int();
// auto sp = make_shared<int>();
// a: process(sp), legal, pass an shared_ptr<int> object to process
// b: process(new int()), illegal, pass an built-int pointer type object to process, shared_ptr does not support implicit tramsform from built-int pointer to shared_ptr type
// c: process(p), illegal, same as b
// d: process(shared_ptr<int>(p)), legal, but not a good practice, directly initializes a shared_ptr object from built-in pointer type, same as a

Exercise 12.13

1
2
3
4
5
6
// auto sp = make_shared<int>();
// auto p = sp.get();
// delete p;
// sp not becomes a dangling pointer, the memory space which is pointed by sp is now freed by expression 'delete p',
// using sp will cause undefined behaviour.

Exercise 12.14

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/////////////////////////////////////////////////////
// struct destination;				   //
// struct connection;				   //
// connection connect(destination);		   //
// void disconnect(connection);			   //
// void end_connection(connection *p)		   //
// {						   //
//   disconnect(*p);				   //
// }						   //
// void f(destination &d)			   //
// {						   //
//   connection c = connect(&d);		   //
//   shared_ptr<connection> p(&c, end_connection); //
// }						   //
/////////////////////////////////////////////////////

Exercise 12.15

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/////////////////////////////////////////////////////
// struct destination;				   //
// struct connection;				   //
// connection connect(destination);		   //
// void disconnect(connection);			   //
// void f(destination &d)			   //
// {						   //
//   connection c = connect(&d);		   //
//   shared_ptr<connection> p(&c,[] (connection* p)//
//                            {disconnect(*p);});  //
// }						   //
/////////////////////////////////////////////////////

Exercise 12.16

1
2
3
4
5
6
7
8
int main()
{
  unique_ptr<int> p(new int(1));
  unique_ptr<int> q(p);		// unique_ptr does not support copy
  unique_ptr<int> x;		// unique_ptr does not support assignment
  x = p;
}

Exercise 12.17

1
2
3
4
5
6
7
8
9
// int ix = 1024, *pi = &ix. *pi2 = new int(2048);
// typedef unique_ptr<int> IntP;
// a: IntP p0(ix); illegal, unique_ptr must be binded to a ptr which is the return value of operator 'new'.
// b: IntP p1(pi); illegal, same reason as a.
// c: IntP p2(pi2); legal, but pi2 may become a dangling pointer.
// d: IntP p3(&ix); illegal, same reason as a.
// e: IntP p4(new int(2048)); legal;
// f: IntP p5(p2.get()); illegal, p2 has not yet release its pointer, poniter is still 'owned' by p2.

Exercise 12.18

1
2
3
4
// specific pointer are 'shared' by shared_type type objects, if new one wants to share, old one does not need to 'release' it, and it is meaningless to provide this member because other shared_ptr that share the same object can still delete it;
// while for unique_ptr, if new one wants to get the control, old one must 'release' the pointed object.
// That is right.

Exercise 12.19

1
2
// See StrBlob.h

StrBlob.h

Exercise 12.20

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
int main(int argc, char *argv[])
{
  if(argc != 2) return -1;
  ifstream ifs(argv[1]);
  if(!ifs) return -1;
  string s;
  StrBlob sb;
  while(getline(ifs, s))
    sb.push_back(s);
  StrBlobPtr beg = sb.begin();
  for(auto count = sb.size(); count > 0; --count)
    {
      cout << beg.deref() << endl;
      beg.incr();
    }
}

Exercise 12.21

1
2
3
4
// The original one is better;
// in function logic there is no difference between them;
// but the original one is much easier to understand.

Exercise 12.22

1
2
// See StrBlob.h

StrBlob.h

Exercise 12.23

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
int main()
{
  /* using unique_ptr to point to the dynamic array
     would make this program much easier */
  // char *
  char *p = new char[20]();
  const char c[] = "hello", d[] = "world";
  strcpy(p, c);
  strcat(p, " ");
  strcat(p, d);
  delete [] p;
  // string
  string s1("hello");
  string s2("world");
  string s = s1 + s2;
}

Exercise 12.24

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
int main()
{
  /* using unique_ptr<char[]> p(new char[length]
     would make this program much easier */
  constexpr size_t length = 10;
  char *p = new char[length];
  string s;
  cin >> s;
  size_t count;
  if(s.length() > length)
    count = length;
  else
    count = s.length();
  for(size_t index = 0; index < count; ++index)
    *(p + index) = s[index];
  // check
  for(size_t index = 0; index < length; ++index)
    cout << *(p + index);
  cout << endl;
  delete [] p;
}

Exercise 12.25

1
2
3
// int *pa = new int[10]
// release: delete [] pa;

Exercise 12.26

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
int main(int argc, char *argv[])
{
  if(argc != 2) return -1;
  size_t n = stoul(argv[1]);
  allocator<string> alloc;
  auto const p = alloc.allocate(n);
  string s;
  string *q = p;
  while(cin >> s && q != p + n)
    *q++ = s;
  const size_t size = q - p;
  // check
  cout << size << endl;
  while(q != p)
    alloc.destroy(--q);
  alloc.deallocate(p, n);
}

Exercise 12.27

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

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
class QueryResult;
class TextQuery
{
  friend QueryResult;
  shared_ptr<vector<string>> sp1;
  shared_ptr<map<string, set<size_t>>> sp2;
 public:
  TextQuery(ifstream &infile);
  QueryResult query(const string &s);
};
TextQuery::TextQuery(ifstream &infile):
sp1(make_shared<vector<string>>()), sp2(make_shared<map<string, set<size_t>>>())
{
  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;
  shared_ptr<vector<string>> sp1;
  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);
}

Exercise 12.28

 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
void read_parse(vector<string> &text, map<string, set<size_t>> &results, ifstream &infile)
{
  size_t line_number = 0;
  string line;
  while(getline(infile, line))
    {
      // store line in vector<string>
      text.push_back(line);
      // scan through the line
      istringstream ist(line);
      string word;
      while(ist >> word)
      results[word].insert(line_number);
      // increase the line_number
      ++line_number;
    }
}
ostream &print(ostream &o, const string &s, const vector<string> &text, const map<string, set<size_t>> results)
{
  set<size_t> line_number;
  auto iter = results.find(s), last = results.end();
  if(iter != last) line_number = iter->second;
  o << "element occurs " << line_number.size() << " times" << endl;
  if(line_number.size())
    {
      for(const auto &l : line_number)
      cout << "        (line " << l + 1 << ") "
	   << text[l] << endl;
    }
  return o;
}
void runQueries(ifstream &infile)
{
  vector<string> text;
  map<string, set<size_t>> results;
  read_parse(text, results, infile);
  while(true)
    {
      cout << "Enter word to look for, or q to quit: ";
      string s;
      if(!(cin >> s) || s == "q") break;
      print(cout, s, text, results) << endl;
    }
}
int main(int argc, char *argv[])
{
  ifstream ifs(argv[1]);
  runQueries(ifs);
}

Exercise 12.29

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// if using while(true), both behave exactly the same,
// if using while condition like below, it is better,
// simple and straight,
// but the drawback is this program would search "q" in text before exit.
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);
}

Exercise 12.30

1
2
// See TextQuery-1230.h

TextQuery-1230.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
/* make some improvements based on the original version
   afger reading through secion 12.3.2 */
class QueryResult;
class TextQuery
{
 public:
  using line_no = vector<string>::size_type;
  TextQuery(ifstream &infile);
  QueryResult query(const string &s);
 private:
  friend QueryResult;
  shared_ptr<vector<string>> sp1;
  shared_ptr<map<string, set<line_no>>> sp2;
};
TextQuery::TextQuery(ifstream &infile):
sp1(new vector<string>), sp2(new map<string, set<line_no>>)
{
  line_no 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
{
  friend ostream &print(ostream &, const QueryResult &);
  string search_word;
  set<TextQuery::line_no> nodata;
  set<TextQuery::line_no> *result = &nodata;
  shared_ptr<vector<string>> sp1;
  shared_ptr<map<string, set<TextQuery::line_no>>> sp2;
 public:
  QueryResult(const string &s, TextQuery &t);
};
QueryResult::QueryResult(const string &s, TextQuery &t):
sp1(t.sp1), sp2(t.sp2), search_word(s)
{
  auto iter = (*sp2).find(search_word);
  auto last = (*sp2).end();
  if(iter != last)
    result = &(iter->second);
}
ostream &print(ostream &o, const QueryResult &q)
{
  auto n = (*q.result).size();
  o << "element occurs " << n << " times" << endl;
  auto result = q.result;
  for(const auto &l : *result)
    cout << "\t(line " << l + 1 << ") "
       << (*q.sp1)[l] << endl;
  return o;
}
QueryResult TextQuery::query(const string &s)
{
  return QueryResult(s, *this);
}

Exercise 12.31

1
2
3
4
// vector type allows multi elements with same value, while set does not;
// so if one word repeats in one single line, the string line would be printed repeatly.
// the version uses set is better, its output is easy to read, more friendly.

Exercise 12.32

1
2
// See TextQuery-1232.h.

TextQuery-1232.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
class QueryResult;
class TextQuery
{
 public:
  using line_no = vector<string>::size_type;
  TextQuery(ifstream &infile);
  QueryResult query(const string &s);
 private:
  friend QueryResult;
  StrBlob sb;
  shared_ptr<map<string, set<line_no>>> sp2;
};
TextQuery::TextQuery(ifstream &infile):
sp2(new map<string, set<line_no>>)
{
  line_no line_number = 0;
  string line;
  while(getline(infile, line))
    {
      // store line in vector<string>
      sb.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
{
  friend ostream &print(ostream &, const QueryResult &);
  string search_word;
  StrBlob qsb;
  set<TextQuery::line_no> nodata;
  set<TextQuery::line_no> *result = &nodata;
  shared_ptr<map<string, set<TextQuery::line_no>>> sp2;
 public:
  QueryResult(const string &s, TextQuery &t);
};
QueryResult::QueryResult(const string &s, TextQuery &t):
qsb(t.sb), sp2(t.sp2), search_word(s)
{
  auto iter = (*sp2).find(search_word);
  auto last = (*sp2).end();
  if(iter != last)
    result = &(iter->second);
}
ostream &print(ostream &o, const QueryResult &q)
{
  auto n = (*q.result).size();
  o << "element occurs " << n << " times" << endl;
  auto result = q.result;
  for(const auto &l : *result)
    {
      ConstStrBlobPtr csbp(q.qsb, l);
      cout << "\t(line " << l + 1 << ") "
	 << csbp.deref() << endl;
    }
  return o;
}
QueryResult TextQuery::query(const string &s)
{
  return QueryResult(s, *this);
}

Exercise 12.33

1
2
// See TextQuery-1233.h

TextQuery-1233.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
class QueryResult;
class TextQuery
{
 public:
  using line_no = vector<string>::size_type;
  TextQuery(ifstream &infile);
  QueryResult query(const string &s);
 private:
  friend QueryResult;
  shared_ptr<vector<string>> sp1;
  shared_ptr<map<string, set<line_no>>> sp2;
};
TextQuery::TextQuery(ifstream &infile):
sp1(new vector<string>), sp2(new map<string, set<line_no>>)
{
  line_no 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
{
  friend ostream &print(ostream &, const QueryResult &);
  string search_word;
  set<TextQuery::line_no> nodata;
  set<TextQuery::line_no> *result = &nodata;
  shared_ptr<vector<string>> sp1;
  shared_ptr<map<string, set<TextQuery::line_no>>> sp2;
 public:
  set<TextQuery::line_no>::iterator begin() {return result->begin();}
  set<TextQuery::line_no>::iterator end() {return result->end();}
  shared_ptr<vector<string>> get_file() {return sp1;}
  QueryResult(const string &s, TextQuery &t);
};
QueryResult::QueryResult(const string &s, TextQuery &t):
sp1(t.sp1), sp2(t.sp2), search_word(s)
{
  auto iter = (*sp2).find(search_word);
  auto last = (*sp2).end();
  if(iter != last)
    result = &(iter->second);
}
ostream &print(ostream &o, const QueryResult &q)
{
  auto n = (*q.result).size();
  o << "element occurs " << n << " times" << endl;
  auto result = q.result;
  for(const auto &l : *result)
    cout << "\t(line " << l + 1 << ") "
       << (*q.sp1)[l] << endl;
  return o;
}
QueryResult TextQuery::query(const string &s)
{
  return QueryResult(s, *this);
}