diff --git a/ch16/README.md b/ch16/README.md index b6d49820..789de7ba 100644 --- a/ch16/README.md +++ b/ch16/README.md @@ -377,4 +377,56 @@ auto sum(T1 a, T2 b) -> decltype(a + b) { } ``` -More safer solution: <[Better `sum`](ex16_41_sum.cpp)> \ No newline at end of file +More safer solution: <[Better `sum`](ex16_41_sum.cpp)> + +## Exercise 16.42 + +> Determine the type of T and of val in each of the following calls: +> ```cpp +> template <typename T> void g(T&& val); +> int i = 0; const int ci = i; +> (a) g(i); +> (b) g(ci); +> (c) g(i * ci); +> ``` + +- (a) `int&` +- (b) `const int&` +- (c) `int` + +## Exercise 16.43 + +> Using the function defined in the previous exercise, what would the template parameter of `g` be if we called `g(i = ci)`? + +`int&` + +## Exercise 16.44 + +> Using the same three calls as in the first exercise, determine the types for `T` if `g`’s function parameter is declared as `T` (not `T&&`). What if `g`’s function parameter is `const T&`? + +Whatever `g`'s function parameter is declared as `T` or `const T&`, the `T`'s type in this three case would always `int`. + +## Exercise 16.45 + +> Given the following template, explain what happens if we call `g` on a literal value such as 42. What if we call `g` on a variable of type `int`? +> ```cpp +> template <typename T> void g(T&& val) { vector<T> v; } +> ``` + +If we call `g` on a literal value such as 42, `T` should be `int`, and we get a tempoary variable `v`, which type is `vector<int>`. If we call `g` on a variable of type `int`, then `val` should be a lvalue, `T` should be `int&`(because `int& &&` ==> `int&`), then we would declared a `v` as `vector<int&>`. But the component type of `vector` must be [assignable](http://en.cppreference.com/w/cpp/concept/CopyAssignable), the references are not assignable, thus, `vector<int&>` is not allowed, the compiler would complain about it. + +## Exercise 16.46 + +> Explain this loop from `StrVec::reallocate` in 13.5 (p.530): +> ```cpp +> for (size_t i = 0; i != size(); ++i) +> alloc.construct(dest++, std::move(*elem++)); +> ``` + +Since C++11, [`std::allocator::construct`](http://en.cppreference.com/w/cpp/memory/allocator/construct)'s second parameter is `Args&&... args`. `*elem++` is a certain lvalue, and would be casted to a rvalue reference by `std::move`, then the `construct` would call the move constructor of `std::string` rather than copy constructor. + +## Exercise 16.47 + +> Write your own version of the flip function and test it by calling functions that have lvalue and rvalue reference parameters. + +[flip and test](ex16_47_flip.cpp) \ No newline at end of file diff --git a/ch16/ex16.42.43.44.45.46/main.cpp b/ch16/ex16.42.43.44.45.46/main.cpp deleted file mode 100644 index fd6e07be..00000000 --- a/ch16/ex16.42.43.44.45.46/main.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/*************************************************************************** - * @file main.cpp - * @author Alan.W - * @date 07 Feb 2014 - * @remark This code is for the exercises from C++ Primer 5th Edition - * @note - ***************************************************************************/ -//! -//! Exercise 16.42: -//! Determine the type of T and of val in each of the following calls: -//! template <typename T> void g(T&& val); -//! int i = 0; const int ci = i; -//! (a) g(i); -// since i is lvalue, T is deduced as int&, val is int& && collapsing to int& -//! (b) g(ci); -// since ci is lvaue, T is deduced as const int&, val is const int& && collapsing to const int& -//! (c) g(i * ci); -// since i * ci is rvalue, T is deduced as int&&, val is int&& && colapsing to int&& -//! -//! Exercise 16.43: -//! Using the function defined in the previous exercise, what would the template -//! parameter of g be if we called g(i = ci)? -// (i = ci) returns lvalue refering to the object i. -// Hence T is deduced as int& val is int& && . -// any change on val changes the object i. -//! -//! Exercise 16.44: -//! Using the same three calls as in the first exercise, determine the types for T -//! if g’s function parameter is declared as T (not T&&). -// ^ -// g(i); -- T is deduced as int -// g(ci); -- T is deduced as int, const is ignored. -// g(i * ci); -- T is deduced as int, (i * ci) returns rvalue which is copied to -// T -//! What if g’s function parameter is const T&? -// ^^^^^^^^ -// g(i) -- T is deduced as int , val : const int& -// g(ci) -- T is deduced as int , val : const int& -// g(i * ci) -- T is deduced as int&&, val : const int&& & collapse to const int& -//! -//! Exercise 16.45: -//! Given the following template, explain what happens if we call g on a literal value -//! such as 42. What if we call g on a variable of type int? -//! template <typename T> void g(T&& val) { vector<T> v; } -//! -//! Discussion on SO: -// http://stackoverflow.com/questions/21624016/when-a-lvalue-is-passed-to-t-what-will-happen -//! -//! relevant section from textbook: -//! When we pass an lvalue (e.g., i) to a function parameter that is an rvalue reference to a -//! template type parameter (e.g, T&&), the compiler deduces the template type parameter as the -//! argument’s lvalue reference type. So, when we call f3(i), the compiler deduces the type of -//! T as int&, not int. -//! -- P.688 -//! -//! In this case, when calling on a literal value,say 42. int&& && will collapse to int&&. At last -//! T is deduced as int. Hence std::vector<T> is instantiated as std::vector<int> which is legal. -//! -//! When calling on a variable int. T will be deduced as int&. int & && will collapse to int&. -//! std::vector<int&> is not legal. The reason why int& can't be element of a vector can be found at: -// http://stackoverflow.com/questions/922360/why-cant-i-make-a-vector-of-references -//! -//! Exercise 16.46: -//! Explain this loop from StrVec::reallocate in § 13.5 (p. 530): -//! -//! for (size_t i = 0; i != size(); ++i) -//! alloc.construct(dest++, std::move(*elem++)); -//! -//! In each iteration, the dereference operator * returns a lvalue which is changed to rvalue by -//! std::move ,becasue the member function construct takes rvalue reference rather than lvalue -//! reference. -//! - - - - -#include <iostream> -#include <vector> -#include <string> - -template <typename T> -void g(T&& val) -{ - std::vector<T> v; -} - -int main() -{ - std::allocator a; - a.construct -} diff --git a/ch16/ex16.47/main.cpp b/ch16/ex16.47/main.cpp deleted file mode 100644 index 926c87ce..00000000 --- a/ch16/ex16.47/main.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/*************************************************************************** - * @file main.cpp - * @author Alan.W - * @date 07 Feb 2014 - * @remark This code is for the exercises from C++ Primer 5th Edition - * @note - ***************************************************************************/ -//! -//! Exercise 16.47: -//! Write your own version of the flip function -//! and test it by calling functions that have lvalue and rvalue reference parameters. -//! - -#include <iostream> -#include <memory> - -void func_lvalue(std::string& lhs, std::string& rhs) -{ - lhs = "Wang\n"; - rhs = "Alan\n"; -} - -void func_rvalue(int&& lhs, int&& rhs) -{ - //! allocate enough space - std::allocator<int> alloc; - int* data(alloc.allocate(3)); - - //! move into the spaced newly allocated - alloc.construct(data , lhs); - alloc.construct(data +1 , 0); - alloc.construct(data +2 , rhs); - - //! print - for (auto p = data; p != data + 3; ++p) - std::cout << *p << "\n"; - - //! destroy and deallocation - for (auto p = data +3; p != data; ) - alloc.destroy(--p); - alloc.deallocate(data,3); -} - -template<typename F, typename T1, typename T2> -void flip(F f, T1&& t1, T2&& t2) -{ - f(std::forward<T2>(t2), std::forward<T1>(t1)); -} - -int main() -{ - //! test for lvalue reference - /* - std::string s1, s2; - flip(func_lvalue, s1, s2); - std::cout << s1 << s2; - */ - - //! test for rvalue reference - flip(func_rvalue, 99,88); - - -} diff --git a/ch16/ex16_47_flip.cpp b/ch16/ex16_47_flip.cpp new file mode 100644 index 00000000..f684f88f --- /dev/null +++ b/ch16/ex16_47_flip.cpp @@ -0,0 +1,24 @@ +#include <iostream> + +void f(int v1, int& v2) +{ + std::cout << v1 << " " << ++v2 << std::endl; +} + +void g(int&& i, int& j) +{ + std::cout << i << " " << ++j << std::endl; +} + +template <typename F, typename T1, typename T2> +void flip(F f, T1&& t1, T2&& t2) +{ + f(std::forward<T2>(t2), std::forward<T1>(t1)); +} + +int main() +{ + int j = 0; + flip(f, j, 42); + flip(g, j, 42); +} \ No newline at end of file