C++Primer第六章:函数

第六章:函数

函数是一个命名了的代码块,我们可以通过调用函数执行相应的代码。

一.函数基础

我们通过调用运算符()来执行函数,调用运算符作用于一个表达式,该表达式是函数或指向函数的指针,()里面放实参。

C++并没有规定实参的求值顺序,所以实参中最好不要改变又使用某个对象。

没有形参当然可以直接不写,为了与C语言兼容,也可以用void表示没有形参,例如 int fun(void)。

在函数中创建的变量是局部变量,如果要延长变量的生存期,可以创建局部静态对象,局部静态对象在程序第一次执行定义的时候初始化,在程序结束时销毁,期间一直有效,使用static定义这样的对象。

函数只能定义一次,但是可以声明多次。

函数声明的时候可以不写形参的名字,也可以写上方便理解。

函数应该在头文件中声音而在源文件中定义。

C++语言支持所谓分离式编译,允许我们把程序分割到几个文件中去,每个文件独立编译,然后再链接到一起。

我们可以直接用命令完成编译链接的所有步骤,也可以先分别把文件编译成.o文件(windows下是.obj),然后再用命令链接.o文件,见练习6.9。

二.参数传递

当形参是引用类型时,我们说实参被引用传递,否则说是被值传递。

在C++中,建议使用引用类型的形参代替指针。

拷贝较大的类类型对象或者容器对象比较低效,甚至有的类类型不支持拷贝操作,这两种时候可以使用引用形参。

当函数无须改变引用形参的值时,最好将其声明为常量引用。

当用实参初始化形参时会忽略掉形参的顶层const,即赋给它常量和非常量都可以,不能使用形参const的区分来重载函数。

形参的初始化和变量的初始化相同,都要底层const匹配。

C++允许我们使用字面值初始化常量引用。

我们尽量使用常量引用,不仅仅因为不让其修改值和减少拷贝,还因为常量引用能够接受更多的实参类型(因为底层const影响拷贝操作,但是如果大多数都能转换为常量引用)。

数组不能拷贝,所以我们不能以值传递的方式传递数组作为参数,但是我们依旧可以把数组作为形参,只是传递的实参为指针而已。

也可以把形参设为数组的引用,例如int (&arr)[10],注意两边的括号不能少。

传递二维数组,可以使用指向数组的指针传递,例如int (*matrix)[10],也可以直接写,例如int matrix[][10]。

main函数中也是可以加参数的,一般是int main(int argc, char *argv[]),第一个指示参数的数量,第二个用C风格字符串指示参数,argv[0]是程序名或空字符串,所以argc至少为1.

有时候参数的数量未知,如果参数的类型相同,我们可以使用initializer_list来存储参数,其是一个模板类,类似于vector,不过其中的元素都是常量。

我们还可以使用C风格的省略符形参(…)来接收不定数量的参数。…只能写在形参列表的最后。且省略符形参应该只使用在C和C++通用的类型,大多数类类型拷贝的时候会出错。

三.返回类型和return语句

return语句终止当前函数并返回调用函数的地方,有两种形式:return; return expr;

值是如何被返回的:返回一个值的方式同初始化一个变量或形参的方式一样,使用返回值初始化调用点的一个临时量。

不能返回局部对象(包括字面值)的引用或指针。为什么在主函数中我们可以创建字面值常量的引用呢,因为主函数字面值常量相当于一个临时量,这个临时量引用后只有函数释放的时候才释放。

C++11新标准规定,函数可以返回花括号包围的值的列表。

cstdlib头文件定义了两个与机器无关的预处理变量用来标识程序执行的成功失败,EXIT_FAILURE和EXIT_SUCCESS。

返回数组指针的方法:函数不能直接返回数组,不过可以返回数组的指针和引用,具体方法如下:

  • 使用类型别名定义数组,然后使用类型别名定义指针,例如typeder int arrT[10]; arrT* func(int i);
  • 声明一个返回数组指针的函数,可以用如下格式声明:type (*func(parm_list)) [dimension]。例如:int (*func(int i)) [10]。
  • 使用尾置返回类型,格式为:auto func(parm_list) -> type
  • 还可以使用decltype,具体见书

其实上面说的返回数组指针都不是值返回数组首地址,而是返回指向数组首地址的指针,这一点要特别注意。

四.函数重载

同一作用域内的几个函数名字相同但是形参列表不同就称之为重载函数。

main函数不能重载。

返回类型和const不能区分函数。

前面说过形参会忽略掉顶层const,因此不能用顶层const形参的区别来重载函数,但是形参不会忽略掉底层const,所以可以用底层

const来重载函数,其实很好理解,对于有无顶层const的形参来说,无论实参是常量还是非常量,两个形参都可以用,所以函数区分不了,对于有无底层const的形参来说,如果实参是常量,则只能调用有底层const的函数,所以可以区分。

在内层作用域中声明的名字将隐藏外层作用域声明的同名实体。C+语言的名字查找发生在类型检查之前,因此当我们在一个作用域中使用某个名字时我们用到的一定是离我们作用域最近的名字,而不管类型是否匹配,即使后面会报错。

五.特殊用途语言特性

我们可以给形参添加默认值,即默认实参,注意一旦某个形参被赋予了默认值,则其后的所有形参都要有默认值。

给默认实参赋值是按照实参的顺序和类型匹配来的,会优先匹配左侧的,因此要为后面的默认实参赋值,必须给前面的参数赋值。

注意在同一个作用域中函数的一个形参只能被赋予依次默认实参,即后续声明和定义都不能再改变之前的默认实参了,但是可以给之前的非默认实参添加默认实参。

不能用局部变量作为默认实参(言下之意就是可以用全局变量作为默认实参且运行时通过改变全局变量的值可以修改默认实参),表达式也可以作为默认实参,默认实参的值是在函数运行的时候确定的。

调用函数一般比求等价表达式要慢一些,因为调用函数有一系列其他工作。

将函数指定为内联函数,通常就是将它在每个调用点上展开,可以用于优化规模较小,流程直接,频繁调用的函数。

内联说明只是向编译器发出的一个请求,编译器可以忽略这个请求。

constexpr函数是指能用于常量表达式的函数,其返回类型和所有形参的类型都得是字面值类型。主要算术类型,指针,引用这些都属于字面值类型。

constexpr函数体中有且只能有一条return语句(注意这句话不是说里面只能有一条return语句,而是除了return语句外不能有其他声明语句之类的)。当然也可以用其他语句,只要这些语句在运行时不执行任何操作就行。

编译器会把对constexpr函数的调用替换成结果值,一般constexpr函数被隐式指定为内联函数。

我们也允许constexpr函数不返回常量表达式。

内联函数和constexpr函数一般定义在头文件中。

C++程序员有时候需要用到一种类似于头文件保护的技术来在开发的时候执行某些调试代码,而在正式执行时屏蔽调试代码,可以使用assert和NDEBUG来实现。

assert是一种预处理宏,即一种预处理变量,其使用方式为用一个表达式作为它的条件:assert(expr)。

assert定义在cassert头文件中,预处理名字由预处理器管理而非由编译器管理,所以我们可以直接使用assert。

assert的行为依赖于名为NDEBUG的预处理变量的状态,如果该变量定义了,则关闭调试状态,assert不执行任何行为。

可以在命令行定义预处理变量,例如

g++ -D NDEBUG  mainc.cc

C++11 编译器和预处理器定义的一些有用的静态变量

__func__是编译器定义的一个局部静态变量(const char的一个静态数组),用于存放函数的名字

下面是预处理器定义的:

__FILE__存放文件名的字符串字面值

__LINE__存放当前行号的整型字面值

__TIME__存放文件编译时间的字符串字面值

__DATE__存放文件编译日期的字符串字面值

上面这些静态变量的使用方式是__var__。

六.函数匹配

大多数时候我们很容易找到匹配的重载函数,但是当函数的参数数量相同,类型又可以互相转换的时候,我们就需要注意函数的匹配了。

如果有多个函数可以选择,则选择实参类型与形参类型最匹配的那个。所谓最匹配是指该函数每个实参的匹配不劣于其他可行函数的匹配且至少有一个实参的匹配优于其他可行函数提供的匹配。如果有多个最匹配的函数,则调用存在二义性,编译器报错。

调用重载函数时应尽量避免强制类型转换,如果确实需要,说明我们重载函数的形参设计的不合理。

最佳匹配的实参类型转换有几个级别,具体见书6.6.1P219

七.函数指针

函数指针的声明格式为:type (*name) (parm_list)

函数名作为值使用时可以自动转换为指针,也就是对于一个func, ptr = func; 和ptr = &func;是一个意思。

我们可以直接使用函数指针调用函数,无需解引用。即ptr()和(*ptr)()是一个意思。

使用重载函数为函数指针赋值时,函数指针的类型(返回类型和形参类型)必须完全与某一个重载函数匹配。

函数也可以作为形参,不过本质是转换成指针使用。

同数组一样,我们也可以返回指向函数的指针,注意我们不能直接返回函数类型,只能返回函数指针类型,方法见书P223。

也可以使用auto和decltype来获取函数指针,注意decltype得到的是函数,要加上*才是函数指针。

练习

6.1

形参出现在函数定义的地方,形参列表可以包含 0 个、1 个或多个形参,多个形参之间以逗号分隔。形参规定了一个函数所接受数据的类型和数量。

实参出现在函数调用的地方,实参的数量与形参一样多。实参的主要作用是初始化形参,并且这种初始化过程是一一对应的,即第一个实参初始化第一个形参、第二个实参初始化第二个形参,以此类推。实参的类型必须与对应的形参类型匹配。

6.2

(a) 错误,返回值的类型与返回类型不匹配,返回类型改为string
(b) 没有返回类型,加上void
(c) 花括号左边没有,加上{;形参名不能相同,后面那个改为v2
(d) 函数体必须在语句块中,加上{}

6.3

#include <iostream>

using namespace std;

int fact(int x) {
  int result = 1;
  while (x > 1) {
    result *= x;
    --x;
  }
  return result;
}

int main() {
  int num;
  cin >> num;

  cout << "num! = " << fact(num) << endl;

  return 0;
}

6.4

见6.3

6.5

#include <iostream>

using namespace std;

int my_abs(int x) {
  if (x >= 0)
    return x;
  else
    return -x;
}

int main() {
  int num;
  cin >> num;

  cout << "abs(num) = " << my_abs(num) << endl;

  return 0;
}

6.6

形参和定义在函数体内部的变量统称为局部变量,它们对函数而言是局部的,仅在函数的作用域内可见。函数体内的局部变量又分为普通局部变量和静态局部变量,对于形参和普通局部变量来说,当函数的控制路径经过变量定义语句时创建该对象,当到达定义所在的块末尾时销毁它。我们把只存在于块执行期间的对象称为自动对象。这几个概念的区别是:

形参是一种自动对象,函数开始时为形参申请内存空间,我们用调用函数时提供的实参初始化形参对应的自动对象。
普通变量对应的自动对象也容易理解,我们在定义该变量的语句处创建自动对象,如果定义语句提供了初始值,则用该值初始化;否则,执行默认初始化。当该变量所在的块结束后,变量失效。
局部静态变量比较特殊,它的生命周期贯穿函数调用及之后的时间。局部静态变量对应的对象称为局部静态对象,它的生命周期从定义语句处开始,直到程序结束才终止。
#include <iostream>

using namespace std;

int my_abs(int x) {
  static int cnt = 0;
  cout << "第" << ++cnt << "次执行my_abs(int x)" << endl;
  if (x >= 0)
    return x;
  else
    return -x;
}

int main() {
  int num;

  while (cin >> num) {
    int result = my_abs(num);
    cout << "abs" << "(" << num << ")" << "=" << result << endl;
  }

  return 0;
}

6.7

int my_abs(int x) {
  static int cnt = 0;
  return cnt++;
}

6.8

文件内容如下:

#ifndef CHAPTER6_H
#define CHAPTER6_H

int fact(int );
int my_abs(int );

#endif // CHAPTER6_H

6.9

Chapter6.h文件内容见6.8

fact.cc:

#include "Chapter6.h"

int fact(int x) {
  int result = 1;
  while (x > 1) {
    result *= x;
    --x;
  }
  return result;
}

fact_main.cc:

#include "Chapter6.h"

#include <iostream>

using namespace std;

int main() {
  int num;
  cin >> num;
  cout << num << "! = " << fact(num) << endl;
  return 0;
}

直接使用命令g++ fact_main.cc,报错

/usr/bin/ld: /tmp/ccjsq20Q.o: in function `main':
fact_main.cc:(.text+0x58): undefined reference to `fact(int)'
collect2: error: ld returned 1 exit status

使用命令g++ fact_main.cc fact.cc -o fact_main成功编译。

使用命令g++ -c fact_main.cc和g++ -c fact.cc得到两个.o文件,再使用命令g++ fact.o fact_main.o -o fact_main得到可执行文件。

6.10

#include <iostream>

using namespace std;

void my_swap(int *x, int *y) {
  int temp = *x;
  *x = *y;
  *y = temp;
}

int main() {
  int num1, num2;
  cin >> num1 >> num2;
  cout << "num1 = " << num1 << "; num2 = " << num2 << endl;
  my_swap(&num1, &num2);
  cout << "num1 = " << num1 << "; num2 = " << num2 << endl;
  return 0;
}

6.11

#include <iostream>

using namespace std;

void reset(int &x) {
  x = 0;
}

int main() {
  int num;
  cin >> num;
  cout << "num: " << num << endl;
  reset(num);
  cout << "num: " << num << endl;
  return 0;
}

3.12

#include <iostream>

using namespace std;

void my_swap(int &x, int &y) {
  int temp = x;
  x = y;
  y = temp;
}

int main() {
  int num1, num2;
  cin >> num1 >> num2;
  cout << "num1 = " << num1 << "; num2 = " << num2 << endl;
  my_swap(num1, num2);
  cout << "num1 = " << num1 << "; num2 = " << num2 << endl;
  return 0;
}

与使用指针相比,使用引用交换变量的内容从形式上看更简单一些,并且无须额外声明指针变量,也避免了拷贝指针的值。

6.13

一个是值参数,一个是引用参数。但是我感觉void f(&T)有点怪,不应该是void f(T&)吗?

6.14

与值传递相比,引用传递的优势主要体现在三方面:

可以直接操作引用形参所引用的对象;
使用引用形参可以避免拷贝大的类类型对象或容器类型对象;
使用引用形参可以帮助我们从函数中返回多个值。
其他时候应该使用值类型。

6.15

s是常量引用是因为不需要改变s的值,occurs是普通引用因为需要改变occurs的值。

s是引用因为其可能是个很大的string对象,拷贝很浪费。occurs是引用是因为我们需要通过它的值得到次数且其为char型拷贝的代价很低。

对于字符出现的次数 occurs 来说,因为需要把函数内对实参值的更改反映在函数外部,所以必须将其定义成引用类型;但是不能把它定义成常量引用,否则就不能改变所引的内容了。

6.16

s在函数没有修改且string&不能接收const string类型的实参,所以应该使用const string&作为形参类型。

6.17

bool HaveUpper(const string& s) {
  for (auto ch : s) {
    if (isupper(ch))
      return true;
    return false;
  }
}

void Tolower(string &s) {
  for (auto &ch : s) {
    if (!islower(ch)) {
      ch = tolower(ch);
    }
  }
}

第一个函数不需要改变string对象,所以设为const string&类型

第二个函数需要改变string对象,所以设为string&类型

6.18

bool compare(matrix& lhs, matriex& rhs) // 判断两个matrix是否相同
vector<int>::iterator change_val(int val, vector<int>::iteratror it) // 返回指向val的迭代器 

6.19

(a) 不合法,参数数量不符
(b) 合法
(c) 合法
(d) 合法

6.20

不需要修改的时候。

可能会造成参数的意外修改,可能造成不能传入某些类型的实参。

6.21

int bigger(const int x, const int *py) {
  return x > *py ? x : *py;
}

6.22

void my_swap(int *(&p), int *(&q)) {
  int *temp = p;
  p = q;
  q = temp;
}

6.23

void print1(int x, int *y);
void print2(const int &x, const int &y);
void print3(int x, int y []);
void print4(int x, int (&y) []);
void print5(int *y, size_t len);
void print6(int *b, int *e);

6.24

函数的目的是输出ia数组中元素的值,但是有可能我们传入的实参数组元素没有十个,所以再增加一个size_t参数,作为循环终止条件。

6.25

#include <iostream>

using namespace std;

int main(int argc, char *argv[]) {
  if (argc < 3) {
    cout << "参数数量不足" << endl;
  }
  else {
    string result = argv[1];
    result += argv[2];
    cout << result << endl;
  }
  return 0;
}

6.26

#include <iostream>

using namespace std;

int main(int argc, char *argv[]) {
  for (int i = 0; i < argc; ++i) {
    cout << "argv[" << i << "] = " << argv[i] << endl; 
  }
  return 0;
}

6.27

int my_sum(initializer_list<int> lst) {
  int sum = 0;
  for (auto i : lst) {
    sum += i;
  }
  return sum;
} 

6.28

const string&

6.29

引用类型的优势主要是可以直接操作引用的对象以及避免拷贝较为复杂的类型对象和容器对象。因为 initializer_list 对象的元素永远是常量值,所以我们不可能通过设定引用类型来更改循环控制变量的内容。只有当 initializer_list 对象的元素类型是类类型或容器类型(比如 string)时,才有必要把范围 for 循环的循环控制变量设为引用类型。

6.30

#include <iostream>
using namespace std;

// 因为含有不正确的返回值,所以这段代码无法通过编译
bool str_subrange(const string &str1, const string str2) {
    // 大小相同:此时用普通的相等性来判断结果作为返回值
    if (str1.size() == str2.size())
        return str1 == str2;        // 正确:== 运算符返回布尔值
    // 得到较短 string 对象大小
    auto size = (str1.size() < str2.size())
                ? str1.size() : str2.size();
    // 检查两个 string 对象的对应字符是否相等,以较短的字符长度为限
    for (decltype(size) i = 0; i != size; ++i) {
        if (str1[i] != str2[i])
            return;                 // 错误 #1:没有返回值,编译器将报告这一错误
    }
    // 错误 #2:控制流可能尚未返回任何值就结束了函数的执行
    // 编译器可能检查不出这一错误
}

int main(int argc, char **argv) {


    return 0;
}

test.cc: In function ‘bool str_subrange(const string&, std::string)’:
test.cc:15:13: error: return-statement with no value, in function returning ‘bool’ [-fpermissive]
   15 |             return;                 // 错误 #1:没有返回值,编译器将报告这一错误
      |             ^~~~~~

6.31

如果引用所引的是函数开始之前就已经存在的对象,则返回该引用是有效的;如果引用所引的是函数的局部变量,则随着函数结束局部变量也失效了,此时返回的引用无效。

如果我们不想返回的引用作为一个左值,则我们应该使用一个常量引用。

6.32

合法的,将ia的值赋值为下标。

6.33

void print(vector<int> &nums, int index) {
  if (index < nums.size()) {
    cout << nums[index] << ' ';
    print(nums, index + 1);
  }
  return;
}

6.34

如果传入的是负数会一直递归下去直到栈溢出。、、

6.35

如果把 val - 1 改成 val-- ,则出现一种我们不期望看到的情况,即变量的递减操作与读取变量值的操作共存于同一条表达式中,这时有可能产生未定义的值。其实无论求值顺序是怎样我都觉得改成val–是有问题的。

6.36

string (&func()) [10];

6.37

typedef string arr[10];
arr& func2();

auto func3() -> string (&) [10];

string str[10];
decltype(str) &func4();

个人觉得都还可以,但是一般都使用类型别名。

6.38

int odd[] = {1, 3, 5, 7, 9};
int even[] = {0, 2, 4, 6, 8};
// 返回一个引用,该引用所引的对象是一个含有 5 个整数的数组。
decltype(odd) &arrPtr(int i) {
  return (i % 2) ? odd : even;		// 返回数组的引用。
}

6.39

(a) 第二条声明语句是想声明形参都有顶层const的重载函数,非法,顶层const不能区分函数。
(b) 非法,返回类型不能区分函数
(c) 合法

6.40

(a) 合法
(b) 非法,默认实参后面的形参必须都有默认实参。

6.41

(a) 非法,没有提供足够的参数
(b) 合法
(c) 合法但与初衷不符,我们是先让ht = 14, bacgrnd = '*',但是char可以转换为int,所以wd = '*'了

6.42

#include <iostream>
#include <string>
using namespace std;

//最后一个形参赋予了默认实参
string make_plural(size_t ctr, const string &word, const string &ending = "s") {
    return (ctr > 1) ? word + ending : word;
}

int main() {
    cout << "success 的单数形式是:" << make_plural(1, "success", "es") << endl;
    cout << "success 的复数形式是:" << make_plural(2, "success", "es") << endl;
    // 一般情况下调用该函数只需要两个实参
    cout << "failure 的单数形式是:" << make_plural(1, "failure") << endl;
    cout << "failure 的复数形式是:" << make_plural(2, "failure") << endl;

    return 0;
}

6.43

(a) 声明和定义都放在头文件中,因为其实内联函数,方便展开且保持定义相同
(b) 声明放头文件,定义放源文件。

6.44

inline bool isShorter(const string &s1, const string &s2) {
    return s1.size() < s2.size();
}

6.45

决定一个函数是否应该是内联函数有很多评判的依据。一般来说,内联机制适用于规模较小、流程直接、频繁调用的函数。一旦函数被定义成内联的,则在编译阶段就展开该函数,以消除运行时产生的额外开销。如果函数的规模很大(比如上百行)不利于展开或者函数只被调用了一两次,那么这样的函数就没必要也不应该是内联的。

6.46

isShorter 函数不符合 constexpr 函数的要求,它虽然只有一条 return 语句,但是返回的结果调用了标准库 string 类的 size() 函数和 < 比较符,无法构成常量表达式,因此不能改写成 constexpr 函数。

6.47

#include <iostream>
#include <vector>
#define NDEBUG
using namespace std;

// 递归函数输出 vector<int> 的内容
void print(vector<int> vInt, unsigned index) {
    unsigned sz = vInt.size();
    // 设置在此处输出调试信息
    #ifndef NDEBUG
    cout << "vector 对象的大小是:" << sz << endl;
    #endif // NDEBUG
    if (!vInt.empty() && index < sz) {
        cout << vInt[index] << endl;
        print(vInt,index + 1);
    }
}

int main() {
    vector<int> v = {1, 3, 5, 7, 9, 11, 13, 15};
    print(v, 0);
    return 0;
}

6.48

该程序对 assert 的使用有不合理之处。在调试器打开的情况下,当用户输入字符串 s 并且 s 的内容与 sought 不相等时,执行循环体,否则继续执行 assert(cin); 语句。换句话说,程序执行到 assert 的原因可能有两个,一是用户终止了输入,二是用户输入的内容正好与 sought 的内容一样。如果用户尝试终止输入(事实上用户总有停止输入结束程序的时候),则 assert 的条件为假,输出错误信息,这与程序的原意是不相符的。

6.49

函数匹配的第一步是选定本次调用对应的重载函数集,集合中的函数称为候选函数。候选函数具备两个典型特征:一是与被调用的函数同名;二是其声明在调用点可见。

函数匹配的第二步是考查本次调用提供的实参,然后从候选函数中选出能被这组实参调用的函数,这些新选出的函数称为可行函数。可行函数也有两个特征:一是其形参数量与本次调用提供的实参数量相等;二是每个实参的类型与对应的形参类型相同或者能转换成形参的类型。

6.50

(a) 不合法,具有二义性
(b) void f(int)
(c) void f(int, int)
(d) void f(double, double = 3.14)

6.51

#include <iostream>
void f();
void f(int);
void f(int, int);
void f(double, double);
using namespace std;

int main() {
    // f(2.56, 42);
    f(42);
    f(42, 0);
    f(2.56, 3.14);

    return 0;
}

void f() {
    cout << "f()" << endl;
}

void f(int i) {
    cout << "f(int)" << endl;
}

void f(int i, int j) {
    cout << "f(int, int)" << endl;
}

void f(double d1, double d2 = 3.14) {
    cout << "f(double, double)" << endl;
}

6.52

(a) 类型提升
(b) 算术类型转换

6.53

(a) 合法,导致const int会匹配第二条语句
(b) 合法,导致const char会匹配第二条语句
(c) 不合法,顶层const不能用来区分重载函数

6.54

int func(int, int);
vector<decltype(func)*> pvec;

6.55

#include <iostream>
#include <cassert>
#include <vector>

using namespace std;

int func1(int a, int b) {
  return a + b;
}

int func2(int a, int b) {
  return a - b;
}

int func3(int a, int b) {
  return a * b;
}

int func4(int a, int b) {
  assert(b != 0);
  return a / b;
}
vector<decltype(func1)*> pvec;


int main() {
  pvec.push_back(func1);
  pvec.push_back(func2);
  pvec.push_back(func3);
  pvec.push_back(func4);

  return 0;
}

6.56

#include <iostream>
#include <cassert>
#include <vector>

using namespace std;

int func1(int a, int b) {
  return a + b;
}

int func2(int a, int b) {
  return a - b;
}

int func3(int a, int b) {
  return a * b;
}

int func4(int a, int b) {
  assert(b != 0);
  return a / b;
}
vector<decltype(func1)*> pvec;


int main() {
  pvec.push_back(func1);
  pvec.push_back(func2);
  pvec.push_back(func3);
  pvec.push_back(func4);

  for (auto func : pvec) {
    cout << func(1, 2) << endl;;
  }

  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值