Primer Plus 函数探幽

引用变量

对于形参为const引用的C++函数,如果实参不匹配,则其行为类似值传递,为确保原始数据不被修改,将使用临时变量来存储值

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

double refcube(const double &v) { // panic without const
return v * v * v;
}

int main() {
long long x = 5;
double res = refcube(x); // then v is a temporary variable
std::cout << res << std::endl;
return 0;
}

因此尽量使用const的原因有:

  • 避免无意中修改不该被修改的变量

  • 使函数能够处理const和非const实参,否则只能接受非const实参

  • 使用const引用让函数能正确生成并使用临时变量

引用一般用于类对象,类设计的语义常常要求使用引用

默认参数

必须从右向左添加默认值

1
2
int f(int x, int y = 1, int z = 1) // valid
int g(int x, int y = 1, int z) // invalid

函数重载

特征标(参数列表)不同的函数可以重载,跟函数返回值类型没关系

函数模板

1
2
3
4
5
6
7
template <typename T>
void swap(T &a, T &b) {
T tmp;
tmp = a;
a = b;
b = tmp;
}

对于同一个函数,并非所有类型都使用同一算法,因此模板定义也可以重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template <typename T>
void swap(T &a, T &b) {
T tmp;
tmp = a;
a = b;
b = tmp;
}
template <typename T>
void swap(T a[], T b[], int n) {
T tmp;
for (int i = 0; i < n; ++i) {
tmp = a[i];
a[i] = b[i];
b[i] = tmp;
}
}

显式具体化:针对模板的一个特定类型提供一个特殊实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
struct job {
char name[40];
double salary;
int floor;
};

template <typename T>
void swap(T &a, T &b);

template <> void swap<job> (job &j1, job &j2);

template <typename T>
void swap(T &a, T &b) {
T temp;
temp = a;
a = b;
b = temp;
}

// 只交换salary和floor字段
template <> void swap<job> (job &j1, job &j2) {
double t1;
int t2;
t1 = j1.salary;
j1.salary = j2.salary;
j2.salary = t1;
t2 = j1.floor;
j1.floor = j2.floor;
j2.floor = t2;
}

优先级:非模板函数 > 具体化 > 常规模板

隐式实例化:有一个常规模板定义,有相关类型调用导致编译器隐式地定义了一个该类型的函数

1
2
3
4
5
6
7
template <typename T>
void print(const T& value) {
std::cout << value << std::endl;
}

print(5); // 编译器自动为int类型生成print函数实例
print("hello"); // 编译器自动为const char *类型生成print函数实例

显式实例化:告诉编译器为模板的特定类型生成实例化代码的过程,而不是等到模板被实际用到时才生成,这可以减少编译时间,因为模板的实例化代码只生成一次,即使在多个地方使用同一模板的相同类型也是如此

1
template void print<int>(const int&);

decltype可以在不知道表达式类型的情况下,将该类型用于变量声明、函数返回类型等场景

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>

template<typename T1, typename T2>
auto f(T1 a, T2 b) -> decltype(a + b) {
return a + b;
}

int main() {
std::cout << f(1, 2) << '\n';
return 0;
}