C++ Primer: Functions
本篇博客主要记录《C++ Primer》一书的第六章 Functions 的相关知识点
Arugment Passing
在定义函数时,如果参数不同是可以重载函数的。但是如果参数时指针/数组,就会容易出现一些小问题。
1
2
3void print(const int*) {}
/* void print(const int[]) {} */
/* void print(const int[10]) {} */比如以上的代码,无论是数组,还是指针参数,其实在处理的时候都会被当成指针来处理,也就是上面的三个看起来参数不同的函数,其实是同一个函数定义。如果取消掉注释,编译的时候是会报重定义的错误的。
那么如果参数不是数组指针,而是数组引用呢?
1
2
3/* void print(int (&arr)[]) {} */
void print(int (&arr)[2]) {}
void print(int (&arr)[10]) {}上面的函数定义其实并不会报重定义的错误,因为参数是不同的,是不同长度的数组引用。值得注意的是,由于是引用,如果不指定长度是会报错的。
在调用上面定义的函数时,也需要注意,对于参数是长度为2的数组引用,传入的数组必须是长度为2的,同样,对于参数是长度为10的数组引用,传入的数组也必须是长度为10的数组。
varying parameters
C 的方法(不建议使用)
在C中可以使用
...
来代表可变长参数来定义函数。这个...
实际上利用了 C 库的varargs
initializer_list 列表参数方法
定义函数时将可变长的部分用
initializer_list<type> xxx
来定义,在使用的时候,通过传入{xxx, xxx, xxx}
来传递可变长参数。值得注意的是,这个initializer_list
中传递的参数都是const
的,是不可以改变的,这一点和使用vector
是不同的,也更加符合参数的语义。举例来说,使用方法大概如下
1
2
3
4
5
6
7
8
9
10void error_msg(initializer_list<string> il) {
for (auto& elem: il) {
cout << elem << " ";
}
cout << endl;
}
int main() {
error_msg({"This", "is", "a", "test"});
}variadic template
return type
在 C/C++ 中,如果函数返回一个指向数组的指针,写法是比较奇怪的。
1
2
3int (*func())[] {
// ...
}要避免这种不太容易识别的写法也是可以的。这本书里提到了很多方法,在
C++11
之前,可以使用typedef
来定义类型别名,当然使用using
也是可以的。1
2
3typedef int arrT[];
using arrT = int[];
arrT *func(int i);除此以外,还可以使用 trailing return type 的方式,就是在函数参数列表之后使用
-> 类型
的方式来表示返回类型。1
auto func(int i) -> int (*)[];
overloading
定义重载函数必须保证参数数量或者参数类型不一样,否则会造成重定义。这里有个值得注意的地方,当参数被
const
修饰的时候,const
必须是low-level const
,top-level const
是会被忽略的,因此对于被top-level const
修饰和未被修饰的参数重载函数会被认为是重定义。1
2
3
4
5
6
7
8
9int func(int);
int func(const int); // 重定义
int func(int *);
int func(int* const); // 重定义
int func(int &);
int func(const int &); // ok
int func(int *);
int func(const int*); // okdefault arguments
默认参数在进行赋值的时候,除了不能使用局部变量,其他表达式都能使用。
inline
inline 函数是程序员希望这个函数会被 compiler 展开,但是 compiler 并不会满足所有的 inline 请求,是否会进行转化还是要看 compiler 的。
constexpr functions