diff --git a/ch16/README.md b/ch16/README.md index d42cd9cf..f349e837 100644 --- a/ch16/README.md +++ b/ch16/README.md @@ -336,10 +336,11 @@ Then, the answers: > The library `max` function has two function parameters and returns the larger of its arguments. This function has one template type parameter. Could you call `max` passing it an int and a double? If so, how? If not, why not? Yes. Specify the parameter explicitly: -> ```cpp -> int a = 6; double b = 6.1231; -> std::cout << std::max(a, b) << std::endl; -> ``` +```cpp +int a = 6; double b = 6.1231; +std::cout << std::max(a, b) << std::endl; +``` + > Normal conversions also apply for arguments whose template type parameter is explicitly specified. ## Exercise 16.38 @@ -394,10 +395,12 @@ More safer solution: <[Better `sum`](ex16_41_sum.cpp)> > (b) g(ci); > (c) g(i * ci); > ``` - -- (a) T: `int&` val: `int& &&` -> `int &` -- (b) T: `const int&` val: `const int& &&` -> `const int &` -- (c) T: `int` val: `int &&` + +```cpp +//T: int& val: int& && -> int & +//T: const int& val: const int& && -> const int & +//T: int val: int && +``` > When we pass an lvalue `int` to a function parameter that is an rvalue reference to a template type parameter `T&&`, the compiler deduces the template type parameter as the argument’s lvalue reference type `int &`. @@ -443,17 +446,17 @@ Since C++11, [`std::allocator::construct`](http://en.cppreference.com/w/cpp/memo ## Exercise 16.49 > Explain what happens in each of the following calls: -```cpp -template void f(T); //1 -template void f(const T*); //2 -template void g(T); //3 -template void g(T*); //4 -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); -``` -> Answer: +>```cpp +>template void f(T); //1 +>template void f(const T*); //2 +>template void g(T); //3 +>template void g(T*); //4 +>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); +>``` + ```cpp g(42); // type: int(rvalue) call template 3 T: int instantiation: void g(int) g(p); // type: int * call template 4 T: int instantiation: void g(int *) @@ -467,6 +470,7 @@ f(p2); // type: const int * call template 2 T:int instantiation: vo ``` ## Exercise 16.50 + > Define the functions from the previous exercise so that they print an identifying message. Run the code from that exercise. If the calls behave differently from what you expected, make sure you understand why. [overload template](ex16_50_overload_template.cpp) @@ -474,6 +478,7 @@ f(p2); // type: const int * call template 2 T:int instantiation: vo ## Exercise 16.51 > Determine what sizeof...(Args) and sizeof...(rest) return for each call to foo in this section. + ```cpp template void foo(const T & t, const Args & ... rest); @@ -485,8 +490,85 @@ foo("hi"); // input in Args: None sizeof...(Args foo(i, s, s, d); // input in Args: string, string, double sizeof...(Args): 3 sizeof...(rest): 3 ``` -# Exercise 16.52 +## Exercise 16.52 + > Write a program to check your answer to the previous question. [variadic template](ex16_52_variadic_template.cpp) +## Exercise 16.54 + +> What happens if we call print on a type that doesn’t have an << operator? + +Error: +```cpp +std::vector v = {1, 2}; +print(v); +// error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream}’ +// and const std::vector’) +// return os << t << endl; +``` + +## Exercise 16.58 + +> Write the emplace_back function for your StrVec class and for the Vec class that you wrote for the exercises in 16.1.2 (p. 668). + +[emplace_back](ex16_58_emplace.cpp) + +## Exercise 16.59 + +> Assuming s is a string, explain svec.emplace_back(s). + +```cpp +template +template +inline void Vec::emplace_back(Args && ... args) +{ + chk_n_alloc(); + alloc.construct(first_free++, std::forward(args)...); +} +Vec vs; +std::string s = "asd"; +vs.emplace_back(s); +// Think (Args && ... args) as (T && t) for only one parameter is passed to the template parameter pack. +// 1: T is deduced as (int &). see Exercise 16.42. +// 2: std::forward(t) returns type (T &&): (int &) -> (int & &&) collapse to (int &). +// 3: call copy constructor of std::string because the second parameter's type is lvalue reference(int &). +``` + +## Exercise 16.61 + +> Define your own version of make_shared. + +[make_shared](ex16_61_make_shared.cpp) + +## Exercise 16.63 + +> Define a function template to count the number of occurrences of a given value in a vector. +Test your program by passing it a vector of doubles, a vector of ints, and a vector of strings. + +[count template](ex16_63_count_template.cpp) + +## Exercise 16.64 + +> Write a specialized version of the template from the previous exercise to handle vector and +a program that uses this specialization. + +[template specialization](ex16_64_template_specialization.cpp) + +## Exercise 16.65 + +> In § 16.3 (p. 698) we defined overloaded two versions of debug_rep one had a const char* +and the other a char* parameter. Rewrite these functions as specializations. + +[debug_rep specialization](ex16_65_debug_rep.cpp) + +## Exercise 16.67 + +> Would defining these specializations affect function matching for debug_rep? If so, how? If not, why not? + +No. It's just an instantiation of the original function template. + +> Specializations instantiate a template; they do not overload it. As a result, +specializations do not affect function matching. + diff --git a/ch16/ex16_53_print.cpp b/ch16/ex16_53_print.cpp new file mode 100644 index 00000000..638395a3 --- /dev/null +++ b/ch16/ex16_53_print.cpp @@ -0,0 +1,36 @@ +#include +#include + +using std::ostream; +using std::cout; +using std::endl; +using std::string; + +template +ostream & print(ostream & os, const T & t) +{ + return os << t << endl; +} + +template +ostream & print(ostream & os, const T & t, const Args& ... rest) +{ + os << t << ", "; + return print(os, rest...); + +} + + +int main() +{ + int i = 2; + double d = 1.23123; + char c = 'c'; + string s = "this is a string"; + cout << "print(cout, i) : " << endl; + print(cout, i); + cout << "print(cout, d, s) : " << endl; + print(cout, d, s); + cout << "print(cout, i, d, s, c, \"this is a C string\") : " << endl; + print(cout, i, d, s, c, "this is a C string"); +} diff --git a/ch16/ex16_58_emplace.cpp b/ch16/ex16_58_emplace.cpp new file mode 100644 index 00000000..ca38124d --- /dev/null +++ b/ch16/ex16_58_emplace.cpp @@ -0,0 +1,331 @@ +#ifndef VC_H +#define VC_H + +#include +#include +#include +#include + +using std::allocator; +using std::uninitialized_copy; +using std::ostream; +using std::cout; +using std::cerr; +using std::endl; +using std::for_each; +using std::swap; +using std::string; +using std::pair; +using std::initializer_list; + +// declaration for below templates . +template class Vec; + +// declaration of each template. +template bool operator==(const Vec &, const Vec &); +template bool operator!=(const Vec &, const Vec &); +template bool operator< (const Vec &, const Vec &); +template bool operator> (const Vec &, const Vec &); +template bool operator<=(const Vec &, const Vec &); +template bool operator>=(const Vec &, const Vec &); + + +template +class Vec +{ + // bound template friend. + friend bool operator==(const Vec &, const Vec &); + friend bool operator!=(const Vec &, const Vec &); + friend bool operator< (const Vec &, const Vec &); + friend bool operator> (const Vec &, const Vec &); + friend bool operator<=(const Vec &, const Vec &); + friend bool operator>=(const Vec &, const Vec &); + + public: + Vec() : elements(nullptr), first_free(nullptr), cap(nullptr) {} + Vec(initializer_list); + Vec(const Vec &); + Vec(Vec &&) noexcept; + Vec & operator=(const Vec &); + Vec & operator=(Vec &&) noexcept; + ~Vec() noexcept; + + T * begin() const; + T * end() const; + size_t size() const; + size_t capacity() const; + T & operator[](size_t); + const T & operator[](size_t) const; + void push_back(const T &); + void push_back(T &&); // used for rvalue + void reserve(size_t); + void resize(size_t, const T & val = T()); + void free(); + + template + void emplace_back(Args && ...); + void swap(Vec &) noexcept; + private: + static allocator alloc; + T * elements; + T * first_free; + T * cap; + pair alloc_n_copy(const T * const &, const T * const &); + void chk_n_alloc(); + void reallocate(size_t max=0); +}; + +template +allocator Vec::alloc; + +template +inline Vec::Vec(initializer_list init_l) : Vec() +{ + pair p = alloc_n_copy(init_l.begin(), init_l.end()); + elements = p.first; + first_free = cap = p.second; +} + +template +inline Vec::Vec(const Vec & v) +{ + pair p = alloc_n_copy(v.begin(), v.end()); + elements = p.first; + first_free = cap = p.second; +} + +template +inline Vec::Vec(Vec && v) noexcept : elements(v.elements), first_free(v.first_free), cap(v.cap) +{ + v.elements = v.first_free = v.cap = nullptr; +} + +template +inline Vec & Vec::operator=(const Vec & v) +{ + if (this != &v) + { + free(); + pair p = alloc_n_copy(v.begin(), v.end()); + elements = p.first; + first_free = cap = p.second; + } + return *this; +} + +template +inline Vec & Vec::operator=(Vec && v) noexcept +{ + if (this != &v) + { + free(); + swap(v); + } + return *this; +} + +template +inline Vec::~Vec() noexcept +{ + if (elements && first_free && cap) + free(); +} + +template +inline T * Vec::begin() const +{ + return elements; +} + +template +inline T * Vec::end() const +{ + return first_free; +} + +template +inline size_t Vec::size() const +{ + return first_free - elements; +} + +template +inline size_t Vec::capacity() const +{ + return cap - elements; +} + +template +inline T & Vec::operator[](size_t index) +{ + if (index > 0 && index < size()) + return *(elements + index); + else + cerr << "Index error:\nmax_index: " << size() - 1 + << " input index: " << index << endl; + exit(EXIT_FAILURE); +} +template +inline const T & Vec::operator[](size_t index) const +{ + if (index > 0 && index < size()) + return *(elements + index); + else + cerr << "Index error:\nmax_index: " << size() - 1 + << " input index: " << index << endl; + exit(EXIT_FAILURE); +} + +template +inline void Vec::push_back(const T & t) +{ + chk_n_alloc(); + alloc.construct(first_free++, t); +} + +template +inline void Vec::push_back(T && t) +{ + chk_n_alloc(); + alloc.construct(first_free++, std::move(t)); +} + +template +inline void Vec::reserve(size_t n) +{ + if (n > capacity()) + reallocate(n); +} + +template +void Vec::resize(size_t n, const T & val) +{ + if (n < size()) + { + while (first_free != elements + n) + alloc.destroy(--first_free); + } + else + { + if (n > capacity()) + reserve(n); + while (first_free != elements + n) + alloc.construct(first_free++, val); + } +} + +template +void Vec::free() +{ + for_each(first_free, first_free, [](T & t) {alloc.destroy(&t);}); + alloc.deallocate(elements, cap - elements); + elements = first_free = cap = nullptr; +} + +template +template +inline void Vec::emplace_back(Args && ... args) +{ + chk_n_alloc(); + alloc.construct(first_free++, std::forward(args)...); +} + +template +inline void Vec::swap(Vec & v) noexcept +{ + std::swap(elements, v.elements); + std::swap(first_free, v.first_free); + std::swap(cap, v.cap); +} + +template +inline pair Vec::alloc_n_copy(const T * const & st, const T * const & end) +{ + T * start = alloc.allocate(end - st); + return {start, uninitialized_copy(st, end, start)}; +} + +template +inline void Vec::chk_n_alloc() +{ + if (size() == capacity()) + reallocate(); +} + +template +void Vec::reallocate(size_t max) +{ + size_t newcapacity = max ? 2 * max : (size() ? 2 * size() : 1); + T * newstart = alloc.allocate(newcapacity); + T * dest = newstart; + for_each(elements, first_free, [&dest](T & elem) {alloc.construct(dest++, std::move(elem));}); + free(); + elements = newstart; + first_free = dest; + cap = newstart + newcapacity; +} + +template +bool operator==(const Vec & v1, const Vec & v2) +{ + return v1.size() == v2.size() && std::equal(v1.begin(), v1.end(), v2.begin()); +} + +template +inline bool operator!=(const Vec & v1, const Vec & v2) +{ + return !(v1 == v2); +} + +template +bool operator<(const Vec & v1, const Vec & v2) +{ + return std::lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end()); +} + +template +bool operator>(const Vec & v1, const Vec & v2) +{ + return v2 < v1; +} + +template +bool operator<=(const Vec & v1, const Vec & v2) +{ + return !(v2 < v1); +} + +template +bool operator>=(const Vec & v1, const Vec & v2) +{ + return !(v1 < v2); +} + + +int main() +{ + // construc and move. + Vec a = {1, 2, 3, 4, 5, 6, 7}; + for_each(a.begin(), a.end(), [](const int & i) {cout << i << endl;}); + Vec b(std::move(a)); + b.push_back(8); + for_each(b.begin(), b.end(), [](const int & i) {cout << i << endl;}); + Vec c = std::move(b); + + // resize. + c.resize(10); + for_each(c.begin(), c.end(), [](const int & i) {cout << i << endl;}); + + // emplace. + c.emplace_back(4); + for_each(c.begin(), c.end(), [](const int & i) {cout << i << endl;}); + + Vec vs; + vs.push_back("first string"); + vs.emplace_back(10, 'c'); + for_each(vs.begin(), vs.end(), [](const string & i) {cout << i << endl;}); +} + +#endif + + diff --git a/ch16/ex16_61_make_shared.cpp b/ch16/ex16_61_make_shared.cpp new file mode 100644 index 00000000..194e4877 --- /dev/null +++ b/ch16/ex16_61_make_shared.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +using std::shared_ptr; +using std::string; +using std::cout; +using std::endl; + +template +shared_ptr make_shared(Args && ... args) +{ + return shared_ptr(new T(std::forward(args)...)); +} + +struct Foo +{ + Foo(int n = 0) noexcept : bar(n) {cout << "Foo: constructor, bar = " << bar << endl;} + ~Foo() {cout << "Foo: destructor, bar = " << bar << endl;} + int getBar() const noexcept {return bar;} +private: + int bar; +}; + +int main() +{ + shared_ptr sp = make_shared(10); + cout << "The first Foo's bar is " << sp->getBar() << endl; + sp.reset(new Foo); + cout << "The second Foo's bar is " << sp->getBar() << endl; +} + diff --git a/ch16/ex16_63_count_template.cpp b/ch16/ex16_63_count_template.cpp new file mode 100644 index 00000000..f25661a7 --- /dev/null +++ b/ch16/ex16_63_count_template.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +using std::vector; +using std::string; +using std::cout; +using std::endl; + + +template +size_t count(const vector & v, T val) +{ + size_t c = 0; + for (auto & elem : v) + { + if (elem == val) + ++c; + } + return c; +} + + +int main() +{ + vector dv = {1, 2, 3, 4, 1, 3, 1, 2, 3, 4, 2, 4}; + vector iv = {2, 2, 1, 4, 2, 1, 4, 5, 6, 4, 3, 4}; + vector sv = {"hello", "bye", "good", "hello", "fun", "hello"}; + + cout << "{1, 2, 3, 4, 1, 3, 1, 2, 3, 4, 2, 4} has: "; + cout << count(dv, 1.0) << " 1" << endl; + + cout << "{2, 2, 1, 4, 2, 1, 4, 5, 6, 4, 3, 4} has: "; + cout << count(iv, 2) << " 2" << endl; + + cout << "{\"hello\", \"bye\", \"good\", \"hello\", \"fun\", \"hello\"} has: "; + cout << count(sv, string("hello")) << " hello" << endl; + +} + diff --git a/ch16/ex16_64_template_specialization.cpp b/ch16/ex16_64_template_specialization.cpp new file mode 100644 index 00000000..b1958ca3 --- /dev/null +++ b/ch16/ex16_64_template_specialization.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +using std::vector; +using std::string; +using std::cout; +using std::endl; + + +template +size_t count(const vector & v, T val) // if the second parameter is (const T &) +{ // a literal char string will be deduced as const char[N] + size_t c = 0; + for (auto & elem : v) + { + if (elem == val) + ++c; + } + return c; +} + +template <> +size_t count(const vector & v, const char * val) +{ + size_t c = 0; + for (auto p : v) + { + if (!std::strcmp(p, val)) + ++c; + } + return c; +} + +int main() +{ + vector dv = {1, 2, 3, 4, 1, 3, 1, 2, 3, 4, 2, 4}; + vector iv = {2, 2, 1, 4, 2, 1, 4, 5, 6, 4, 3, 4}; + vector sv = {"hello", "bye", "good", "hello", "fun", "hello"}; + + cout << "{1, 2, 3, 4, 1, 3, 1, 2, 3, 4, 2, 4} has: "; + cout << count(dv, 1.0) << " 1" << endl; + + cout << "{2, 2, 1, 4, 2, 1, 4, 5, 6, 4, 3, 4} has: "; + cout << count(iv, 2) << " 2" << endl; + + cout << "{\"hello\", \"bye\", \"good\", \"hello\", \"fun\", \"hello\"} has: "; + cout << count(sv, string("hello")) << " hello" << endl; + + vector cv = {"hello", "bye", "good", "hello", "fun", "hello"}; + cout << "{\"hello\", \"bye\", \"good\", \"hello\", \"fun\", \"hello\"} has: "; + cout << count(sv, string("hello")) << " hello" << endl; +} + diff --git a/ch16/ex16_65_debug_rep.cpp b/ch16/ex16_65_debug_rep.cpp new file mode 100644 index 00000000..a9fbe04a --- /dev/null +++ b/ch16/ex16_65_debug_rep.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +using std::string; +using std::ostringstream; +using std::cout; +using std::endl; + + +template +string debug_rep(T t) +{ + ostringstream ret; + ret << t; + return ret.str(); +} + +template <> +string debug_rep(const char * t) +{ + return t; +} + +template <> +string debug_rep(char * t) +{ + return t; +} + + +int main() +{ + string s = "this is a string"; + cout << debug_rep("this is a literal string") << endl; + cout << debug_rep(s) << endl; + cout << debug_rep('c') << endl; +} diff --git a/ch17/README.md b/ch17/README.md index fbea6d8d..bbd1d05f 100644 --- a/ch17/README.md +++ b/ch17/README.md @@ -108,4 +108,59 @@ Because `std::accumulate`'s third parameter is the initial value of the sum. It' > What would happen if your regex object in the previous program were initialized with "[^c]ei"? Test your program using that pattern to see whether your expectations were correct. -`[^c]ei` says we want any such letter that is followed by the letters 'ei', This pattern describes strings containing exactly **three** characters. The test words in [ex17_15](ex17_15.cpp) will all fail. \ No newline at end of file +`[^c]ei` says we want any such letter that is followed by the letters 'ei', This pattern describes strings containing exactly **three** characters. The test words in [ex17_15](ex17_15.cpp) will all fail. + +## Exercise 17.17 + +> Update your program so that it finds all the words in an input sequence that violiate the “ei” grammar rule. + +[regex](ex17.17.18/main.cpp) + +## Exercise 17.19 + +> Why is it okay to call m[4].str() without first checking whether m[4] was matched? + +> str() returns a string containing the matched poertion of the input.If not matched returns the empty string whcih can also be used for comparison. + +## Exercise 17.20 + +> Write your own version of the program to validate phone numbers. + +[validate phone numbers](ex17.19.20/main.cpp) + +## Exercise 17.21 + +> Rewrite your phone number program from § 8.3.2 (p. 323) to use the valid function defined in this section. + +[validate phone numbers](ex17.21/main.cpp) + +## Exercise 17.22 + +> Rewrite your phone program so that it allows any number of whitespace characters to separate the three parts of a phone number. + +[validate phone numbers](ex17.22/main.cpp) + +## Exercise 17.28 + +> Write a function that generates and returns a uniformly distributed random unsigned int each time it is called. + +[uniformly distribution](ex17.28.29.30/main.cpp) + +## Exercise 17.29 + +> Allow the user to supply a seed as an optional argument to the function you wrote in the previous exercise. + +[uniformly distribution](ex17.28.29.30/main.cpp) + +## Exercise 17.30 + +> Revise your function again this time to take a minimum and maximum value for the numbers that the function should return. + +[uniformly distribution](ex17.28.29.30/main.cpp) + +## Exercise 17.33 + +> Write a version of the word transformation program from §11.3.6 (p. 440) that allows multiple transformations +for a given word andrandomly selects which transformation to apply. + +[word transformation](ex17_33_word_transformation.cpp) diff --git a/ch17/ex17.22/ex17.22/main.cpp b/ch17/ex17.22/main.cpp similarity index 100% rename from ch17/ex17.22/ex17.22/main.cpp rename to ch17/ex17.22/main.cpp diff --git a/ch17/ex_33.cpp b/ch17/ex17_33_word_transformation.cpp similarity index 100% rename from ch17/ex_33.cpp rename to ch17/ex17_33_word_transformation.cpp