Chapter 16 template and generic programming of C++ Primer
Section 16.5 answers to template specific exercises
Exercise 16.62: define your own version of hash < Sales_data > and define a sales_ Unordered of data object_ multiset. Save multiple transactions to a container and print their contents.
[Topic setting ideas]
This exercise template is special.
[answer]
#ifndef PROGRAM16_62_SALES_DATA_H #define PROGRAM16_62_SALES_DATA_H #include <string> #include <iostream> // unchanged from chapter 14 except for added friend declaration for hash class Sales_data { friend class std::hash<Sales_data>; friend std::ostream &operator<< (std::ostream&, const Sales_data&); friend std::istream &operator>>(std::istream&, Sales_data&); friend bool operator==(const Sales_data &, const Sales_data &); friend std::ostream &print(std::ostream&, const Sales_data&); friend std::istream &read(std::istream&, Sales_data&); public: // constructors Sales_data() = default; Sales_data(const std::string &s): bookNo(s) { } Sales_data(const std::string &s, unsigned n, double p): bookNo(s), units_sold(n), revenue(p*n) { } Sales_data(std::istream &); std::string isbn() const { return bookNo; } Sales_data& operator+=(const Sales_data&); private: double avg_price() const; std::string bookNo; unsigned units_sold = 0; double revenue = 0.0; }; namespace std { template <> // we're defining a specialization with struct hash<Sales_data> // the template parameter of Sales_data { // the type used to hash an unordered container must define these types typedef size_t result_type; typedef Sales_data argument_type; // by default, this type needs == size_t operator()(const Sales_data& s) const; // our class uses synthesized copy control and default constructor // other members as before }; } // close the std namespace; note: no semicolon after the close curly // non-member Sales_data operations inline bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs) { return lhs.isbn() < rhs.isbn(); } inline bool operator==(const Sales_data &lhs, const Sales_data &rhs) { return lhs.isbn() == rhs.isbn() && lhs.units_sold == rhs.units_sold && lhs.revenue == rhs.revenue; } inline bool operator!=(const Sales_data &lhs, const Sales_data &rhs) { return !(lhs == rhs); } Sales_data add(const Sales_data&, const Sales_data&); std::ostream &print(std::ostream&, const Sales_data&); std::istream &read(std::istream&, Sales_data&); Sales_data operator+(const Sales_data&, const Sales_data&); std::ostream &operator<<(std::ostream&, const Sales_data&); std::istream &operator>>(std::istream&, Sales_data&); #endif // PROGRAM16_62_SALES_DATA_H
#include "program16_62_sales_data.h" #include <string> using std::istream; using std::ostream; // define the hash interface for Sales_data namespace std { size_t hash<Sales_data>::operator()(const Sales_data& s) const { return hash<string>()(s.bookNo) ^ hash<unsigned>()(s.units_sold) ^ hash<double>()(s.revenue); } } // close the std namespace; note: no semicolon after the close curly // remaining members unchanged from chapter 14 Sales_data::Sales_data(istream &is) { is >> *this; // read a transaction from is into this object } double Sales_data::avg_price() const { if (units_sold) return revenue/units_sold; else return 0; } // member binary operator: left-hand operand is bound to the implicit this pointer // assumes that both objects refer to the same book Sales_data& Sales_data::operator+=(const Sales_data &rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } // assumes that both objects refer to the same book Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; // copy data members from lhs into sum sum += rhs; // add rhs into sum return sum; } istream &operator>>(istream &is, Sales_data &item) { double price; // no need to initialize; we'll read into price before we use it is >> item.bookNo >> item.units_sold >> price; if (is) // check that the inputs succeeded item.revenue = item.units_sold * price; else item = Sales_data(); // input failed: give the object the default state return is; } ostream &operator<<(ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price(); return os; } // operators replace these original named functions istream &read(istream &is, Sales_data &item) { double price = 0; is >> item.bookNo >> item.units_sold >> price; item.revenue = price * item.units_sold; return is; } ostream &print(ostream &os, const Sales_data &item) { os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price(); return os; } Sales_data add(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; // copy data members from lhs into sum sum += rhs; // add rhs into sum return sum; }
#include <cstddef> using std::size_t; #include <string> using std::string; #include <iostream> using std::cin; using std::cout; using std::endl; #include <unordered_set> using std::unordered_multiset; #include <functional> #include "program16_62_sales_data.h" using std::hash; int main() { //Use hash < Sales_data > and Sales_data = = operator unordered_multiset<Sales_data> SDset; Sales_data item; while(cin >> item){ SDset.insert(item); } cout << "SDset.size = " << SDset.size() << endl; for(auto sd: SDset) cout << sd << endl; return 0; }
Operation results:
Exercise 16.63: define a function template and count the number of occurrences of a given value in a vector. Test your function and pass it a double vector, an int vector and a string vector.
[Topic setting ideas]
This exercise defines template functions.
[answer]
#include <iostream> #include <vector> #include <cstring> using namespace std; template <typename T> int occur(vector<T> &vec, const T &v) { int ret = 0; for(auto a:vec) { if(a == v) ret++; } return ret; } template <> int occur(vector<char *> &vec, char *const &v) { int ret = 0; for(auto a:vec) { if(!strcmp(a, v)) ret++; } return ret; } int main() { vector<double> vd = {1.1, 3.14, 2.2, 3.14, 3.3, 4.4}; cout << "occur(vd, 3.14)=" << occur(vd, 3.14) << endl; vector<int> vi = {0, 1, 2, 3, 4, 5}; cout << "occur(vi, 0)=" << occur(vi, 0) << endl; vector<string> vs = {"Hello", "World", "!"}; cout << "occur(vs, string(end))=" << occur(vs, string("end")) << endl; vector<char *> vp; vp.push_back(new char[6]); vp.push_back(new char[6]); vp.push_back(new char[2]); strcpy(vp[0], "Hello"); strcpy(vp[1], "World"); strcpy(vp[2], "!"); char *w = new char[6]; strcpy(w, "World"); cout << "occur(vp, w)=" << occur(vp, w) << endl; delete []w; delete vp[2]; delete vp[1]; delete vp[0]; return 0; }
Operation results:
Exercise 16.64: write a special case version of the template in the previous question to deal with vector < const char * >. The writer uses this specialized version.
[Topic setting ideas]
This exercise defines a special version.
[answer]
See the previous question. Note that when the exceptional version is commented out, the last occur call will match the general version and compare char * with = = so that the equivalent C-style string cannot be found.
Exercise 16.65: in section 16.3 (page 617), we defined two overloaded debugs_ Rep version, one accepts const char * parameter and the other accepts char * parameter. Rewrite these two functions to a special case version.
[Topic setting ideas]
This exercise defines a special version.
[answer]
The two specialized versions are as follows. See exercise 16.48 for the complete procedure.
template<> std::string debug_rep(char *p) { return debug_rep(std::string(p)); } template <> std::string debug_rep(const char *cp) { return debug_rep(std::string(cp)); }
Exercise 16.66: reload debug_ What are the advantages and disadvantages of rep function compared with special case?
[Topic setting ideas]
Understand the difference between specialization and overloading.
[answer]
Overloading will affect function matching, that is, the compiler will take the new overloaded version as one of the candidates to select the best match during function matching. This requires careful design to avoid that the actual matching is not as good as we want. Specialization does not affect function matching. It does not provide a new choice for the compiler for function matching, but provides a special definition different from the original template for a special instance of the template. In essence, it takes over part of the instantiation work of the compiler after function matching. That is, when a template is the best match and needs to be instantiated as this special instance, it is no longer instantiated from the original template, but the special version is directly used. Therefore, the specialization is simpler - when a template does not meet our requirements, we only need to design a specialized version that meets the requirements.
Exercise 16.67: defining exceptional versions affects debug_ Does the rep function match? If not, why?
[Topic setting ideas]
Understand specialization.
[answer]
Above.