Skip to content

Commit bf686cb

Browse files
committed
Update: adjust auto related discriptions
1 parent 7481fed commit bf686cb

File tree

4 files changed

+150
-54
lines changed

4 files changed

+150
-54
lines changed

src/zh/02-program-structure/scope.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ using namespace_name :: name;
331331

332332
整个 `using` 声明组成了一个[声明语句](./statements.md#声明语句)`using` 声明可以在任何作用域内使用。
333333

334-
`using` 声明引入的名称,可以在引入的作用域内直接使用,不需要有限定名称。例如:
334+
`using` 声明引入名称后,在这个作用域剩下的范围内,使用 `name` 就如同使用 `namespace_name :: name` 一样。例如:
335335
```cpp
336336
namespace A {
337337
int a = 1;

src/zh/03-types/overload.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
---
2+
title: 3.6 初识重载
3+
---
4+
5+
## `auto` 参数的函数
6+
7+
::: important TODO: 润色与细化描述
8+
:::
9+
10+
在声明函数类型时,并不能推导出函数调用类型为何,因此在声明函数类型时,必须显式指定参数类型,而不能使用 `auto`。例如:
11+
```cpp
12+
using binary_int_func = int(auto, auto); // [!code error] // 错误,auto 不能用在这里
13+
```
14+
15+
但是,我们可以通过函数类型来确定一个声明中有 `auto` 的函数的形式:
16+
```cpp
17+
auto add(auto a, auto b) { // 这个函数的参数和返回值类型尚不确定
18+
return a + b;
19+
}
20+
21+
using binary_int_func = int(int, int);
22+
int apply(binary_int_func& func, int a, int b) {
23+
return func(a, b);
24+
}
25+
26+
int result = apply(add, 2, 3); // 正确,用 add 初始化 func
27+
```
28+
这里,使用 `add` 初始化 `func` 时,会被推导为 `int(int, int)`,从而确定了 `add` 的参数和返回值类型。
29+
30+
::: info 你的第一个模板函数
31+
考虑如下的代码:
32+
```cpp
33+
auto add(auto a, auto b) {
34+
return a + b;
35+
}
36+
```
37+
技术性地说,这里的 `add` 函数是一个函数模板,而非函数。当函数存在参数的类型(返回值没有影响)声明为 `auto` 时,这个函数就是一个模板。
38+
这样设计使得这个 `add` 可以适应于各种各样的参数,并且以和计算结果相同的方式返回出来。
39+
40+
`add` 函数的两个参数类型被确定时,这个函数模板就会被自动推导成为一个对应的函数,例如:
41+
```cpp
42+
auto result = add(1, 2); // result 是 int 类型
43+
auto result2 = add(1.0, 2.0); // result2 是 double 类型
44+
auto result3 = add(1, 2.0); // result3 是 double 类型
45+
```
46+
你可能会觉得,这不就是把运算符 `+` 变成了个函数吗,但我们可以考虑一些更复杂的计算,例如[简单的控制台程序](../02-program-structure/cli-program.md)中的 `sqrt` 函数:
47+
```cpp
48+
auto sqrt(auto x) {
49+
auto a = x;
50+
while (a * a > x) {
51+
a = (a + x / a) / 2;
52+
}
53+
return a;
54+
}
55+
56+
auto result = sqrt(314159ull); // result 是 unsigned long long 类型
57+
```
58+
这时候,无论 `x` 是什么整数类型,`sqrt` 函数都能选择对应类型的局部对象 `a` 的类型,并返回对应类型的结果。而不需要费劲写下 `sqrt` `sqrtus` `sqrtul` `sqrtull` 等一系列函数,不用在调用的时候推敲要用哪个函数,不用再担惊受怕会不会因为类型不匹配发生数据溢出错误。
59+
:::
60+
61+
## 函数重载
62+
63+
在函数的[前向声明](../02-program-structure/function.md#前向声明)中,读者可能已经注意到,如果前向声明和函数定义的参数列表不一致,似乎并不会直接导致编译错误。
64+
这是因为 C++ 的一个影响深远的特性:**函数重载**。
65+
66+
目前,介绍的内容已经足够对函数重载进行初步的理解,简单来说,函数重载是指,函数名相同时但参数列表不同时,可以定义多个这样的函数。在调用的时候,会选择最为匹配的函数。例如:
67+
68+
```cpp
69+
// 为了简便,这里只给出函数声明
70+
int add(int a, int b);
71+
double add(double a, double b);
72+
73+
int main() {
74+
int a = 1, b = 2;
75+
float c = 1.0, d = 2.0;
76+
int result1 = add(a, b); // 调用 int add(int, int)
77+
double result2 = add(c, d); // 调用 double add(double, double)
78+
}
79+
```
80+
81+
不过,当无法判断哪个函数更为匹配时,编译器会报错。例如:
82+
83+
```cpp
84+
// 为了简便,这里只给出函数声明
85+
void foo(long a);
86+
void foo(double a);
87+
88+
int main() {
89+
foo(1); // [!code error] // 无法判断调用哪个函数
90+
}
91+
```
92+
93+
## 重载决议
94+
95+
函数重载的选择过程称为**重载决议**。重载决议由如下的步骤组成:
96+
97+
1. 建立候选函数集合
98+
2. 去除不可行的候选函数
99+
3. 选择最佳的候选函数
100+
101+
### 建立候选函数集合
102+
103+
候选函数集合是指所有与调用的函数名相同的函数。例如:
104+
105+
```cpp
106+
// 为了简便,这里只给出函数声明
107+
108+
void foo(int a);
109+
void foo(double a);
110+
111+
namespace example {
112+
void foo(long a);
113+
}
114+
115+
int main() {
116+
int int_value = 1;
117+
long long_value = 1l;
118+
119+
foo(int_value); // 候选函数集合为 {foo(int), foo(double)}
120+
121+
{
122+
using namespace example;
123+
foo(long_value); // 候选函数集合为 {foo(int), foo(double), example::foo(long)}
124+
}
125+
}
126+
```
127+
128+
注意,[`using` 声明](../02-program-structure/scope.md#using-声明)并不是增添地引入名称,而是让没有 `name_space::` 限定的使用如同有限定的使用,这在重载的意义上是有区别的。例如:
129+
130+
```cpp
131+
// 为了简便,这里只给出函数声明
132+
void foo(int a);
133+
void foo(double a);
134+
135+
namespace example {
136+
void foo(long a);
137+
}
138+
139+
int main() {
140+
using example::foo;
141+
foo(1); // 候选函数集合仅有 {example::foo(long)}
142+
}
143+
```
144+
145+
在上面的例子中,`foo(1)` 会调用 `example::foo(long)`,而不是 `foo(int)` 或 `foo(double)`。因为这里 `foo` 的调用如同 `example::foo` 一样,是一个限定的调用。
146+
147+
###

src/zh/03-types/reference-type/function-ref.md

Lines changed: 1 addition & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: 3.5.2 函数引用
33
---
44

5-
可以使用引用绑定到函数
5+
引用也可以用来指代一个函数,这种引用称为**函数引用**。例如
66

77
```cpp
88
int add(int a, int b) {
@@ -65,57 +65,6 @@ apply(add, 2, 3); // [!code error] // 错误,add 类型不匹配
6565
```
6666
这样,在调用 `apply` 函数时,就会提示无法用 `a` 初始化 `apply` 的第一个参数。这在 `apply` 函数的实现比较复杂的时候能够有效的提升分析错误的效率。
6767
68-
在声明函数类型时,并不能推导出函数调用类型为何,因此在声明函数类型时,必须显式指定参数类型,而不能使用 `auto`。例如:
69-
```cpp
70-
using binary_int_func = int(auto, auto); // [!code error] // 错误,auto 不能用在这里
71-
```
72-
73-
但是,我们可以通过函数类型来确定一个声明中有 `auto` 的函数的形式:
74-
```cpp
75-
auto add(auto a, auto b) { // 这个函数的参数和返回值类型尚不确定
76-
return a + b;
77-
}
78-
79-
using binary_int_func = int(int, int);
80-
int apply(binary_int_func& func, int a, int b) {
81-
return func(a, b);
82-
}
83-
84-
int result = apply(add, 2, 3); // 正确,用 add 初始化 func
85-
```
86-
这里,使用 `add` 初始化 `func` 时,会被推导为 `int(int, int)`,从而确定了 `add` 的参数和返回值类型。
87-
88-
::: info 你的第一个模板函数
89-
考虑如下的代码:
90-
```cpp
91-
auto add(auto a, auto b) {
92-
return a + b;
93-
}
94-
```
95-
技术性地说,这里的 `add` 函数是一个函数模板,而非函数。当函数存在参数的类型(返回值没有影响)声明为 `auto` 时,这个函数就是一个模板。
96-
这样设计使得这个 `add` 可以适应于各种各样的参数,并且以和计算结果相同的方式返回出来。
97-
98-
`add` 函数的两个参数类型被确定时,这个函数模板就会被自动推导成为一个对应的函数,例如:
99-
```cpp
100-
auto result = add(1, 2); // result 是 int 类型
101-
auto result2 = add(1.0, 2.0); // result2 是 double 类型
102-
auto result3 = add(1, 2.0); // result3 是 double 类型
103-
```
104-
你可能会觉得,这不就是把运算符 `+` 变成了个函数吗,但我们可以考虑一些更复杂的计算,例如[简单的控制台程序](../02-program-structure/cli-program.md)中的 `sqrt` 函数:
105-
```cpp
106-
auto sqrt(auto x) {
107-
auto a = x;
108-
while (a * a > x) {
109-
a = (a + x / a) / 2;
110-
}
111-
return a;
112-
}
113-
114-
auto result = sqrt(314159ull); // result 是 unsigned long long 类型
115-
```
116-
这时候,无论 `x` 是什么整数类型,`sqrt` 函数都能选择对应类型的局部对象 `a` 的类型,并返回对应类型的结果。而不需要费劲写下 `sqrt` `sqrtus` `sqrtul` `sqrtull` 等一系列函数,不用在调用的时候推敲要用哪个函数,不用再担惊受怕会不会因为类型不匹配发生数据溢出错误。
117-
:::
118-
11968
::: info 直接声明函数引用类型与旧式类型别名
12069
在前面,我们为了限制函数引用的类型,使用了一个类型别名来表达函数类型:
12170
```cpp

src/zh/03-types/reference-type/object-ref.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: 3.5.1 对象引用
33
---
44

5-
**引用**是 C++ 中的一种特殊类型,它用来指代一个对象,或者一个函数。例如:
5+
**引用**是 C++ 中的一种特殊类型,它可以用来指代一个对象。例如:
66

77
```cpp
88
int a = 42;

0 commit comments

Comments
 (0)