Skip to content

Commit

Permalink
finish ch16
Browse files Browse the repository at this point in the history
  • Loading branch information
applenob committed Mar 3, 2019
1 parent 5ef6e97 commit c4372ab
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 89 deletions.
Binary file added cpp_source/ch16/ex_16_51
Binary file not shown.
25 changes: 25 additions & 0 deletions cpp_source/ch16/ex_16_51.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <iostream>

using namespace std;

template <typename T, typename ... Args>
void foo(const T &t, const Args& ... rest){
cout << "sizeof...(Args): " << sizeof...(Args) << endl;
cout << "sizeof...(rest): " << sizeof...(rest) << endl;
};

void test_param_packet(){
int i = 0;
double d = 3.14;
string s = "how now brown cow";

foo(i, s, 42, d);
foo(s, 42, "hi");
foo(d, s);
foo("hi");
}

int main(){
test_param_packet();
return 0;
}
163 changes: 153 additions & 10 deletions excersize/ch16.md
Original file line number Diff line number Diff line change
Expand Up @@ -1404,7 +1404,8 @@ template <typename T> void g(T&& val) { vector<T> v; }
## 练习16.46
> 解释下面的循环,它来自13.5节中的 StrVec::reallocate:
> 解释下面的循环,它来自13.5节中的 `StrVec::reallocate`:
```cpp
for (size_t i = 0; i != size(); ++i)
alloc.construct(dest++, std::move(*elem++));
Expand Down Expand Up @@ -1462,9 +1463,9 @@ template<typename T> std::string debug_rep(T* p)
```cpp
template <typename T> void f(T);
tempalte <typename T> void f(const T*);
tempalte <typename T> void g(T);
tempalte <typename T> void g(T*);
template <typename T> void f(const T*);
template <typename T> void g(T);
template <typename T> void g(T*);
int i = 42, *p = &i;
const int ci = 0, *p2 = &ci;
g(42); g(p); g(ci); g(p2);
Expand All @@ -1490,18 +1491,63 @@ f(42); f(p); f(ci); f(p2);
解:


## 练习16.51

> 调用本节中的每个 `foo`,确定 `sizeof...(Args)``sizeof...(rest)`分别返回什么。
解:

```cpp
#include <iostream>

using namespace std;

template <typename T, typename ... Args>
void foo(const T &t, const Args& ... rest){
cout << "sizeof...(Args): " << sizeof...(Args) << endl;
cout << "sizeof...(rest): " << sizeof...(rest) << endl;
};

void test_param_packet(){
int i = 0;
double d = 3.14;
string s = "how now brown cow";

foo(i, s, 42, d);
foo(s, 42, "hi");
foo(d, s);
foo("hi");
}

int main(){
test_param_packet();
return 0;
}
```
结果:
```
sizeof...(Args): 3
sizeof...(rest): 3
sizeof...(Args): 2
sizeof...(rest): 2
sizeof...(Args): 1
sizeof...(rest): 1
sizeof...(Args): 0
sizeof...(rest): 0
```
## 练习16.52
> 编写一个程序验证上一题的答案。
解:
参考16.51。
## 练习16.53
> 编写你自己版本的 `print` 函数,并打印一个、两个及五个实参来测试它,要打印的每个实参都应有不同的类型。
Expand Down Expand Up @@ -1600,11 +1646,11 @@ Vec<T>::emplace_back(Args&&...args)
解:

```cpp
template <typename T, typename ... Args>
auto make_shared(Args&&... args) -> std::shared_ptr<T>
{
return std::shared_ptr<T>(new T(std::forward<Args>(args)...));
}
template <typename T, typename ... Args>
auto make_shared(Args&&... args) -> std::shared_ptr<T>
{
return std::shared_ptr<T>(new T(std::forward<Args>(args)...));
}
```
## 练习16.62
Expand All @@ -1613,32 +1659,129 @@ Vec<T>::emplace_back(Args&&...args)
解:
## 练习16.63
> 定义一个函数模版,统计一个给定值在一个`vecor`中出现的次数。测试你的函数,分别传递给它一个`double`的`vector`,一个`int`的`vector`以及一个`string`的`vector`。
解:
```cpp
#include <iostream>
#include <vector>
#include <cstring>
// template
template<typename T>
std::size_t count(std::vector<T> const& vec, T value)
{
auto count = 0u;
for(auto const& elem : vec)
if(value == elem) ++count;
return count;
}
// template specialization
template<>
std::size_t count (std::vector<const char*> const& vec, const char* value)
{
auto count = 0u;
for(auto const& elem : vec)
if(0 == strcmp(value, elem)) ++count;
return count;
}
int main()
{
// for ex16.63
std::vector<double> vd = { 1.1, 1.1, 2.3, 4 };
std::cout << count(vd, 1.1) << std::endl;
// for ex16.64
std::vector<const char*> vcc = { "alan", "alan", "alan", "alan", "moophy" };
std::cout << count(vcc, "alan") << std::endl;
return 0;
}
```

## 练习16.64

> 为上一题的模版编写特例化版本来处理`vector<const char*>`。编写程序使用这个特例化版本。
解:

参考16.64。

## 练习16.65

> 在16.3节中我们定义了两个重载的 `debug_rep` 版本,一个接受 `const char*` 参数,另一个接受 `char *` 参数。将这两个函数重写为特例化版本。
解:

```cpp
#include <iostream>
#include <vector>
#include <cstring>
#include <sstream>

// template
template <typename T>
std::string debug_rep(T* t);

// template specialization T=const char* , char* respectively.
template<>
std::string debug_rep(const char* str);
template<>
std::string debug_rep( char *str);

int main()
{
char p[] = "alan";
std::cout << debug_rep(p) << "\n";
return 0;
}


template <typename T>
std::string debug_rep(T* t)
{
std::ostringstream ret;
ret << t;
return ret.str();
}

// template specialization
// T = const char*
template<>
std::string debug_rep(const char* str)
{
std::string ret(str);
return str;
}

// template specialization
// T = char*
template<>
std::string debug_rep( char *str)
{
std::string ret(str);
return ret;
}
```
## 练习16.66
> 重载`debug_rep` 函数与特例化它相比,有何优点和缺点?
解:
重载函数会改变函数匹配。
## 练习16.67
> 定义特例化版本会影响 `debug_rep` 的函数匹配吗?如果不影响,为什么?
解:
解:
不影响,特例化是模板的一个实例,并没有重载函数。
117 changes: 38 additions & 79 deletions notes/ch16.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,97 +127,56 @@

### 理解std::move

### 转发

## 重载与模板

## 可变参数模板

## 模板特例化

| | |
|-----|-----|
| | |
| | |
| | |
| | |
| | |
| | |

****

****

****

****

****

****

****

****

****

****
- 标准库`move`函数是使用右值引用的模板的一个很好的例子。
- 从一个左值`static_cast`到一个右值引用是允许的。

****
```cpp
template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
return static_cast<typename remove_reference<T>::type&&>(t);
}
```

****

****

****

****

****

****

****

****

****

****

****

****

****

****

****
### 转发

****
- 使用一个名为`forward`的新标准库设施来传递参数,它能够保持原始实参的类型。
- 定义在头文件`utility`中。
- 必须通过显式模板实参来调用。
- `forward`返回显式实参类型的右值引用。即,`forward<T>`的返回类型是`T&&`

****
## 重载与模板

****
- 多个可行模板:当有多个重载模板对一个调用提供同样好的匹配时,会选择最特例化的版本。
- 非模板和模板重载:对于一个调用,如果一个非函数模板与一个函数模板提供同样好的匹配,则选择非模板版本。

****
## 可变参数模板

****
**可变参数模板**就是一个接受可变数目参数的模板函数或模板类。
- 可变数目的参数被称为参数包。
- 模板参数包:标识另个或多个模板参数。
- 函数参数包:标识另个或者多个函数参数。
- 用一个省略号来指出一个模板参数或函数参数,表示一个包。
- `template <typename T, typename... Args>``Args`第一个模板参数包。
- `void foo(const T &t, const Args& ... rest);``rest`是一个函数参数包。
- `sizeof...`运算符,返回参数的数目。

****
### 编写可变参数函数模板

****
- 可变参数函数通常是递归的:第一步调用处理包中的第一个实参,然后用剩余实参调用自身。

****
### 包扩展

****
- 对于一个参数包,除了获取它的大小,唯一能做的事情就是**扩展**(expand)。
- 扩展一个包时,还要提供用于每个扩展元素的**模式**(pattern)。

****
### 转发参数包

****
- 新标准下可以组合使用可变参数模板和`forward`机制,实现将实参不变地传递给其他函数。

****
## 模板特例化(Specializations)

****
- 定义函数模板特例化:关键字`template`后面跟一个空尖括号对(`<>`)。
- 特例化的本质是实例化一个模板,而不是重载它。特例化不影响函数匹配。
- 模板及其特例化版本应该声明在同一个头文件中。所有同名模板的声明应该放在前面,然后是特例化版本。
- 我们可以部分特例化类模板,但不能部分特例化函数模板。

0 comments on commit c4372ab

Please sign in to comment.