C++语言基础——函数(详解)

Posted 我爱OJ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++语言基础——函数(详解)相关的知识,希望对你有一定的参考价值。

目录

函数是什么

函数的定义

主函数举例

 空函数

函数的调用

语法格式

举例说明:计算x的n次方

递归

递归的主要点

递归的结构

条件

递归使用的场景


函数是什么

函数全名叫计算机函数,它可以帮助你完成一些特定的程序。你可以把它简单理解成一把手枪。

手枪的作用:完成几发子弹进行射击的功能

手枪的结构:有固定的几个部分构成

所以,手枪是具有一定格式,可以完成一些特定功能的一件工具,这就是函数

函数的定义

主函数举例

int main()
    cout << "Hello world!\\n";
    return 0;

由上得,在创建函数时,必须编写其定义。所有函数定义包括以下组成部分:

  • 名称:每个函数都必须有一个名称。通常,适用于变量名称的规则同样也适用于函数名称。
  • 形参列表:调用函数的程序模块可以向其发送数据。形参列表是保存传递给函数的值的变量列表。如果没有值传递给函数,则其形参列表为空。
  • 主体:函数的主体是处理函数正在执行的任务的一组语句。这些语句包含在一组大括号中。
  • 返回类型:函数可以将值发送回调用它的程序模块。返回类型是要发送回的值的数据类型。

 语法格式

返回值类型 函数名称 (参数列表)

	 函数体语句;
	 return 返回值;

 空函数

如前所述,函数可以返回一个值。教程中介绍过的所有程序中的 main 函数都被声明为向操作系统返回一个 int 值。"return 0;" 语句使得当 main 函数完成执行时返回值 0

然而,并不是所有函数都一定要返回值。某些函数只需执行一个或多个语句,然后返回。在 C++中,这样的函数称为空函数举个例子

void shuchu()

    cout << "Hello world!\\n";

 这个函数的名称为shuchu,它的命名方式和变量一样,函数应该按这种方式命名,即通过名称揭示其功能。因为该函数不需要接收任何信息以执行其任务,所以它的括号中没有形参列表

该函数的返回类型是 void。这意味着函数在完成执行后不返回值,并返回到调用该程序的部分。因为没有返回值,所以不需要 return 语句。当函数中的语句己经完成执行并且遇到结束函数的封闭大括号时,程序将自动返回

函数的调用

功能:使用已定义的函数

语法格式:

函数名称 (参数列表)

调用函数将导致函数的执行。函数 main 在程序启动时自动调用,但所有其他函数必须由函数调用语句执行。当一个函数被调用时,程序分支到该函数并执行其主体中的语句。 

举例说明:计算x的n次方

#include<stdio.h>
#include<math.h>
double power(double x,int n)

  int m;
  m=pow(x,n);
  return m;

int main()

  double x,y;
  int n;
  scanf("%d%lf",&n,&x);
  y=power(x,n);
  printf("%.2f",y);
  return 0;

运行结果:

在第3行,我们定义了一个名字叫power的函数,它的实际作用是求x的n次方,函数体内,使用了math库里的一个函数——pow函数,它的作用就是求x的n次方。在函数结束时,返回一个m的值

与所有 C++ 程序一样,该程序是从 main 函数开始执行的,其他函数只有在它们被调用时才执行。在上面程序中,函数 power 由 main 函数中的以下语句调用:

power(x,n);

递归

递归就是指一段程序直接或者间接调用自身的一种方法,通过这种形式执行需要进行一些固定步骤的许多操作,它可以把一个复杂并且庞大的问题简单化,通过专注于解决它分化出来的小问题从而解决大问题,从而大大减少我们的代码量,是提高我们编码效率的很好方法(递归就是容易理解,但是内存太高,大型考试建议不要用递归(除非不得不用,实在是做不出来))

递归的主要点

  • 递归函数的参数和返回值
  • 终止条件
  • 递归的逻辑设计

举个例子(求阶乘):

int factorial(int n)
    if(n==1)
        return n;
    else
        return n*factorial(n-1);

int main()

    int n;
    cout<<"请输入整数:"<<endl;
    scanf("%d",&n);
    cout<<"整数:"<<n<<"的阶乘为:"<<factorial(n)<<endl;
    cout<<"\\n"<<endl;
    return 0;

这个看着不难,不就是个连乘吧?细想头大,就是让我用计算器去敲也要半天吧。

但有了递归,这个事情就好说了:

这个可以看成二个数相乘:100×(99×98×97×……×4×3×2×1)

括号内绿色的乘积看作一个数,简单了吧。

如果你还嫌烦,括号内的数也可以同样看成二个数相乘

99×(98×97×……×4×3×2×1)

你没有没发现点什么?对的,我们就把一个相对复杂的式子看作一个整体,不去考虑它具体的值

大家继续思考下,如果上面的括号里我还是嫌复杂呢?是不是还可以继续分成二个数相乘呢?答案是肯定可以的。

那分解到什么时候可以直接得到答案呢?

对的,如果分解成2×1,我们就可以轻松解决了。

那么3×(2×1)也就解决了。同理:

4×(3×2×1)也就解决了。同理呢?

大家应该能想到5×(4×3×2×1)也解决了,继续扩大就可以慢慢解决。

100×(99×98×97×……×4×3×2×1)

递归的结构

    public void fun(参数) 
 
    if (终止条件) 
 
    return;
 
    
 
    fun(参数);
 
    (其他判断条件或语句);
 
    

在上边代码中,当第一次进入函数时,先判断是否符合终止条件,符合则直接结束函数,不符合入下一语句调用自己重新进入下一层自身函数,(注意这是最外一层将不向下继续执行语句,外层卡在fun(参数处)),这个调用自己进入自身函数的操作过程即为“递”的过程。假设进入下一层后符合终止条件,返回结果,此时之前进入自身函数执行完成返回最外一层函数,最外一层函数递归调用处得到结果,(即内层函数执行完成得到结果返回值),这个过程即为“归”的过程。这时最外一层函数才能继续执行下一语句,直至函数运行完成。

条件

递归必须具备两个条件

  • 一是有边界,即终止条件。

  • 二是需要调用自己。

递归使用的场景

1.大问题可以拆分为多个子问题

2.原问题和拆分后的子问题除了数据规模不同,解决思路完全相同

3.存在递归终止条件

递归在线性数据结构中使用不太明显,迭代基本可以很容易地解决问题。

递归在非线性结构中非常重要,比如二叉树,回溯,典型的树形问题-九宫格字母组合

使用函数时常见报错

1 重定义变量             

Error:redefinition of 'a'

2  缺少分号

Error:expected ';' after expression 

3 数组维数错误

Error:array type 'int [101]' is not assignable

4  关于 if 与 else

Error:expected expression

Warning: equality comparison result unused [-Wunused-comparison]

if 判断里不能有分号!

5  关于 if 与 else

这个是把等号写成了赋值号

 Warning: using the result of an assignment as a condition without parentheses [-Wparentheses]

这个特别坑!!!

6  括号匹配错误

Error: expected ']'

Error: expected ']'

Error: extraneous closing brace ('')

7  关于字符串的输入错误 (*)

Error:  invalid operands to binary expression ('std::istream' (aka 'basic_istream<char>') and 'char *')
        cin>>c+1;
        ~~~^ ~~~

Warning: operator '>>' has lower precedence than '+'; '+' will be evaluated first [-Wshift-op-parentheses]
        cin>>c+1;
           ~~~^~

Note: candidate function template not viable: no known conversion from 'std::istream' (aka 'basic_istream<char>') to 'std::byte' for 1st argument
  operator>> (byte  __lhs, _Integer __shift) noexcept
  ^

8  写错函数 / 变量名

Error: use of undeclared identifier 'mam'; did you mean 'max'?

少年易老学难成,一寸光阴不可轻。未觉池塘春草梦,阶前梧叶已秋声 

C/C++: C++可调用对象详解

  C++中有几种可调用对象:函数,函数指针,lambda表达式,bind创建的对象,以及重载了函数调用符的类。
1. 函数
  函数偏基础的东西,在这里不再叙述。重点讲下C++11的某些重要特性和函数指针。
 
  可变形参函数:
  C++11有两种办法传递可变形参(其实学了模板以后可以用模板的自动递归来展开所传递的参数,这个后面再说)。
  1. 第一种是initializer_list,这是一个标准库类型(其实是个模板)。
std::initializer_list<T>{ };

  可以使用列表初始化来进行初始化,T表示的是参数类型,initializer_list可以被拷贝,但是一定要注意的是,它是一种引用拷贝,也就是说拷贝后新的list和被拷贝的list是的元素都是共享的。

 

  2. 第二种是省略符形参,熟悉C的人对这个也应该很熟悉了,就是printf和scanf这些函数所用的方法。

void print(int, ...);

  省略符形参其实是为了方便访问特殊的C代码而设计的,这些代码其实是使用了标准库varargs的功能(C标准库)。

  如果我们使用这种方法传递省略形参,一定要注意,这种代码只能用来处理C++和C通用版本的东西,对于C++内的对象,这种方法大多数情况下都是不行的。
 1 #include <stdarg.h>
 2 void method(int i, ...)
 3 {
 4     int v;
 5     va_list params;//声明可变參数列表
 6     va_start(params, i); //依据可变參数前的參数获得可变參数的起始地址
 7     do {
 8         v = va_arg(params, int); //从可变參数列表中获得參数的值,类型为int
 9         printf("%d", v);
10     } while (v != 0);
11     va_end(params);
12 }
13 
14 void format(const char* format, ...)
15 {
16     char buf[1024];va_list params;
17     va_start(params, format);
18     vsnprintf_s(buf, 1024, 1024, format, params);
19     va_end(params);
20     printf("%s", buf);
21 }
  (注意省略符前面的逗号可以省略。)
 
  注意C++内,函数可以返回引用(但是不能返回局部对象的引用,类的成员函数还可以指定返回左值还是返回右值),另外C++11还支持列表初始化返回。
  C++可以返回数组,我们可以使用别名来返回数组(一定要知道数组的维度)。
typedef int arr[10];//typedef方法
using Arr = int[10];//别名用法
Arr *func0(int i);
int(*func1(int i))[10];
auto func2(int i) -> int(*)[10];
  
  另外C++还可以有尾置返回类型:
1 //尾置返回类型(明确知道函数返回的指针指向什么地方)
2 int odd[] = { 1,3,5,7,9 };
3 int even[] = { 0,2,4,6,8 };
4  
5 decltype(odd) *arrPtr(int i)
6 {
7     return (i % 2) ? &odd : &even;
8 }
 
  C++重要的特性之一:函数重载(main函数不能被重载),函数重载需要满足形参列表不同的条件(但要注意编译器无法区别顶层const,如果有两个相同位置且相同类型的参数,编译器并不会区分它们的顶层const的区别,但是底层const会区别)。如果我们传递一个非常量对象或者一个指向非常量对象的指针的时候,编译器会优先选择非常量对象。
 1 const std::string &shortString(const std::string &s1, const std::string &s2)
 2 { 
 3     return s1.size() <= s2.size() ? s1 : s2; 
 4 }
 5 std::string &shortString(std::string &s1, std::string &s2)
 6 {
 7     auto &r = shortString(const_cast<const std::string &>(s1),
 8         const_cast<const std::string &>(s2));//注意这里的shortSring必须要const_cast
 9                                              //因为在这里s1和s2都是非常连版本,而编译器会优先选择非常量版本进行调用,导致递归出错
10     return const_cast<std::string &>(r);
11 }

const_cast和重载(注意自己一定要清楚const_cast之前的变量是不是const,如果是const,那么就会产生未定义的行为)。

  函数的默认实参,从左往右,从某个形参开始起,如果它被赋予了默认形参,那么其后面的所有形参都要赋予默认形参,默认形参在给定的作用域内只能被赋予一次(即使函数有可能被多次声明,但是后续声明不能为已经添加过默认形参的形参添加默认形参)。而且要注意,局部变量是不能去定义默认形参的。primer 5e上面的例子:
 1 size_t wd = 80;char def = \' \';
 2 size_t ht();
 3 string screen(size_t = ht(), size_t = wd, char = def);
 4 string window = screen();//调用screen(ht(), 80 \' \')
 5 
 6 void f2() 
 7 {
 8     def = \'*\';
 9     size_t wd = 100;
10     window = screen();//调用screen(ht(), 80 \'*\')
11 }
  
  内联函数和constexpr函数:简单来说,内联函数可以在汇编的时候把函数体展开,条件就是函数体必须要短小,不过很多时候就算你指定了函数为内联函数,编译器并不会承认,在很多编译器中,内联inline关键字是一个可选选项,编译器并不会强制执行iniline操作。
  对于constexpr函数,其是一个C++11用于之指定常量表达式的方法constexpr函数需要满足:返回类型和所有形参类型都是字面值类型,而且函数体必须有且只有一条return语句,因为constexpr函数只有返回值,所以constexpr被隐式指定为内联函数,constexpr函数也可以包含其他语句,只要这些语句不执行任何操作就可以了(比如可以有空语句,以及类型别名)。
1 constexpr size_t scale(size_t cnt) 
2 { 
3     using test = int;
4     return cnt * 2; 
5 }
 
要注意,C++允许constexpr函数返回一个非常量值,而且当constexpr函数的实参不是字面值常量的时候,返回值有可能不是常量表达式。
int t;
int arr[scale(100)];
auto ret = scale(t);

  scale(100)返回的是一个常量表达式,但是scale(t)则返回的就是size_t类型。

  内联函数和constexpr函数都应该放在头文件里面,内联函数和constexpr函数与其他函数最大的不同就是,内联函数和constexpr函数是可以多次定义的,但是这多次定义必须完全一致,所以内联函数和constexpr函数一般都定义在头文件内。
 
  函数匹配和二义性问题:由于C++由函数重载,所以函数匹配是一件很让编译器头疼的事情,编译器将实参类型到形参类型的转换分为以下几个等级:(从上到下优先级依次降低)。
  1. 精确匹配(实参类型和形参类型是一样的,或者实参从数组类型或者函数类型转换成对应的指针类型,向实参添加顶层const或者从实参中消除顶层const)(对于底层const,如果同时定义了非常量版本和常量版本,如果传入非常量,那么就会调用非常量版本函数,如果传入的实参为常量版本,则会调用常量版本的函数)。
  2. 通过const实现的匹配
  3. 通过类型提升来实现的匹配(比如整型提升)。
  4. 通过算术转换或者指针转换实现的匹配。
  5. 通过类类型转来来匹配的转换。
 
 
2. 函数指针
  1. 函数指针顾名思义就是指向函数的指针,类似于
int(*pf)(std::string s1);
  指针名的括号必不可少,不然会认为是返回int的指针,使用函数指针可以直接让指针等于函数名,C++规定给函数名取地址和直接使用函数名都是可以得到函数的地址:
1 int test(std::string s1) { return 0; }
2 int(*pf)(std::string s1);
3 pf = test;
4 pf = &test;
5 test(std::string());
6 (*test)(std::string());
  上面的两种使用方法都是等价的。同时在C++11中,我们也可以使用尾置返回类型来声明一个函数的返回值为一个函数指针(和数组指针类似)
auto foo() -> int(*)(std::string s1);
  也可以用decltype来指定返回的函数指针的类型(注意函数名的前面一定显式添加星号表明返回的是一个函数指针而不是函数本身)
decltype(test) *getfcn();

 

 

3. lambda表达式(匿名函数对象,一个C++的语法糖)


  一个lambda表达式表示一个可调用的代码单元,我们可以将其理解为有一个未命名的内联函数,与任何函数类似,一个lambda具有一个返回类型,一个参数列表和一个函数体,并且这个函数体是可以定义在函数内部的。

  lambda必须使用尾置返回类型来指定返回类型,不能有默认形参,并且具有以下形式:
        [capture list](parameter list) -> return type{function body }
   lambda表达式可以忽略返回类型和形参,形如:
auto fcn = [] {return 42;};//定义了一个可调用对象fcn为一个lambda表达式
               //忽略参数列表,忽略返回类型(都为空)
  有一些标准库的算法的谓词,其形参参数可能不能满足我们的需求,这个时候我们可以使用lambda表达式的参数捕获来间接获得更多的参数,比如标准库的<algorithm>里面有find_if函数,它接受一个一元谓词,可以在规定范围内找到满足条件的第一个迭代器,假设现在我们给定一个vector<string>,我们要找到在整个vector内不大于长度l的第一个迭代器,如果我们一定要使用find_if这个标准库函数来实现这个功能,这个时候我们就要用到lambda的参数捕获了。
  先来个例子:
 1 #include <algorithm>
 2 #include <iostream>
 3 #include <vector>
 4 
 5 using std::vector;
 6 using std::find_if;
 7 using std::for_each;
 8 using std::cout;
 9 using std::endl;
10 
11 void searchSegment(vector<int> nums, const int floorSize, const int ceilSize)
12 {
13     std::stable_sort(nums.begin(), nums.end(),
14         [](const int &a, const int &b) { return a < b;});//注意sort和stable_sort当comp(x,x)的时候一定要返回false
15     auto segFloorIndex = find_if(nums.begin(), nums.end(),
16         [&floorSize](const int &a) ->bool { return a >= floorSize; });
17     auto segCeilIndex = find_if(nums.begin(), nums.end(),
18         [&](const int &a) { return a >= ceilSize; });
19     cout << "The size of the segment is " << segCeilIndex - segFloorIndex << endl;
20 }

  捕获分值捕获和引用捕获,引用捕获就在变量前面加&就可以了,引用捕获可以解决某些类不能被拷贝的问题(比如输入输出流),另外捕获还可以是隐式的,比如我上面的算segCeilIndex的时候,就可以直接一个&就代表捕获所有的引用参数了。

  另外捕获可以混用(引用和值捕获混用),这个时候捕获列表的第一个元素必须是&(引用捕获)或者=(值捕获)(不能同时隐式值捕获和隐式引用捕获,只能是其中一种情况),如果才用了一种隐式捕获情况,另一种捕获必须显示,比如采用了隐式引用捕获,那么值捕获的所有值都必须显式捕获。
  lambda表达式值捕获的值也是可以变的,只是这个时候必须使用mutable关键字,(非常量引用捕获理所当然可以修改,这个不用多说)比如:
int v;
auto f = [v]()mutable {return ++v; };
  有些时候lambda表达式的尾置返回类型不能被省略,最常见的就是if_else的情况发生(?:表达式是可以省略返回参数的),就例如上面的segFloorIndex加了个->bool。
  lambda表达式质上是一个重载了函数调用符的类(看5),很适合在一些函数短小而且只用写几次的地方,可以把代码写得很好看。
 
 
 
4. bind绑定的对象

  我们上面看到lambda表达式可以很好地解决标准库有些函数的谓词问题,现在我们想问的问题是,如果不想用lambda来实现谓词的转换,我只想用函数,那么怎么办呢?标准库提供了一个很好的办法,那就是bind函数(C++11 Feature)。(定义在头文件functional里面)。
  简单的来说,bind可以把一些固定的参数和函数绑定,然后调用函数的时候相当于被绑定的参数就不用填了,相当于减少了传入参数的数量,比如上面的例子我们可以这么改。
1 bool findMin(const int &a, const int &value) 
2 {
3     return a >= value; 
4 }
5 segFloorIndex = find_if(nums.begin(), nums.end(), bind(findMin, _1, floorSize));
6 segCeilIndex = find_if(nums.begin(), nums.end(), bind(findMin, _1, ceilSize));

  其中_1(类似的还有_2,_3....)这个东西为占位符(有点像汇编的那个,定义在std的placeholders的作用域),表示的是外面的可以传入的参数,参数从左到右依次填入_1,_2...中(比如如果有调用对象G的定义为auto G= bind(f, _2, a, _1 ),调用G(x,y)实际上是f(Y,a,x ),用这个拿来交换参数位置)。

  有些参数bind不能直接绑定(比如万恶的输入输出流不允许拷贝),那就用标准库的ref函数来得到这个对象的引用就可以了(cref是得到常量引用,这两个都在functional的头文件中)。
 
 
 
5. 重载了函数调用符的类
  C++的类厉害的地方之一就是可以重载函数运算,就是retType operator( )(parameter...){ }的形式,这种重载了函数调用符的类可以直接让我们用类名来实现函数的功能,比如:
1 class FunctonalClass
2 {
3 public:
4     bool operator()(const int &a, const int &value)
5     {
6         return a >= value;
7     }
8 };
  当然了上面的这个例子我是故意写成这样的,其实和lambda调用形式长得很像,其实lambda就是相当于重载了函数调用符的类去掉了类名而已,事实上lamba也是在做类的事情,比如在3的lambda其实是这样展开的:
1 class AnonymityFunctional
2 {
3 public:
4     AnonymityFunctional(const int &v) :value(v) { }
5     bool operator()(const int &a) const { return a >= value; }
6 private:
7     const int &value;
8 };
  调用形式:
segFloorIndex = find_if(nums.begin(), nums.end(), AnonymityFunctional(floorSize));
segCeilIndex = find_if(nums.begin(), nums.end(), AnonymityFunctional(ceilSize));
  lambda表达式产生的类不含默认构造函数,赋值运算符和默认析构函数,它是否含有默认的拷贝和移动函数那就要视捕获的数据成员来定。
  事实上这种函数对象再functional里面一大把,看看标准库就知道了,标准库的函数对象一般为类模板,比如greator<T>。
 
 
 
利用std::function创建可调用对象的集合

  上面说了5种可调用对象,有时候我们想实现某些设计模式的时候,我们想用一个统一接口来调用这些对象,实现可调用对象表,但是这些可调用对象本质上类型都是不一样的,如何把他们统一起来呢?答案就是使用标准库模板function<T>,
  最简单的,我们可以把function的统一接口:
std::function<bool(const int &)>
  这个时候,可以添加形如返回值的bool,形参为const int&的对象进去了,无论是lambda还是函数指针还是类调用对象:
 1 bool fcn(const int &a){ return a; }
 2 
 3 class FunctionTest
 4 {
 5 public:
 6     FunctionTest() = default;
 7 
 8     bool getWhat(const int &a)const { return true; }
 9     bool getS()const { return true; }
10 };
11 
12 std::function<bool(const int &)> f1 = [](const int &a) { return a ; };
13 std::function<bool(const int &)> f2 = Test();
14 std::function<bool(const int &)> f3 = fcn;

  最常见的就是把一些函数放进map里面,那样我们就可以根据关键字来调用对象了,非常方便。不过需要注意的是,如果函数是重载过的,那我们就不能直接塞函数名进去了(会产生二义性),这个时候就要自己创建要放入函数的函数指针,再创建function。

1 bool fcn() { return true; }
2 bool fcn(const int &a){ return a; }
3 
4 bool(*pfcn)(const int &) = fcn;
5 std::function<bool(const int &)> f3 = pfcn; 
  
  关于类成员函数指针转换为可调用对象的问题:
  说实话这个指针方法很少用到(不过在Qt上可以用这个来实现信号和槽的绑定,写的时候比用SIGNAL和SLOT好,当然这种写法信号和槽不能有函数重载),类成员函数指针可以用来指向类的非静态函数(也就是类成员指针使用的时候一定要绑定类对象),因为成员函数指针必须用*.或者是->*先给类解引用了才可以使用,所以本质上成员函数指针不是可调用对象,但是我们仍然可以用function或者bind来是它成为可调用对象。
  不过说实话这个东西确实有点偏,先扯一下用法把,假设一个类里面有一个类:
1 class FunctionTest
2 {
3 public:
4     FunctionTest() = default;
5 
6     bool getWhat(const int &a)const { return true; }
7     bool getS()const { return true; }
8 };
  然后我们这样定义一个成员函数指针,让它指向getWhat
1 FunctionTest haha, *phaha = &haha;
2 
3 bool (FunctionTest::*pf)(const int &) const;//先定义一个指针
4 pf = &FunctionTest::getWhat;//指针指向类的什么函数
5 
6 auto ret = (haha.*pf)(floorSize);
7 auto ret1 = (phaha->*pf)(ceilSize);
  *.和->*是用来定义指向成员的,*.给对象用,->*给指向对象的指针用,这个很好理解(这里我只是用来演示了一下找成员函数,其实找成员也是这样找的)。
  当然了上面的写法是为了应付有函数重载的情况,如果没有函数重载,那么auto肯定是最好的拉~
auto pf = &FunctionTest::getWhat;
  好了扯了那么多那怎么用fcuntion来生成成员函数指针的可调用对象呢?很简单,直接:
std::function<bool(const FunctionTest *, const int &)> f4(&FunctionTest::getWhat);

  这里我们发现了要生成成员函数的可调用对象必须带对象,所以生成的可调用对象都必须带类的对象,这很符合成员函数指针的要求——必须绑定对象,事实上,当我们使用这样的可调用对象的时候,就相当于把this指针先传给第一个参数了,所以这个function对象本质上是需要两个参数的!不能用于find_if。

  当然了,生成一个这样的的调用对象,除了用function,还可以用标准库的另一个函数:mem_fn
auto f = std::mem_fn(&FunctionTest::getWhat);
  生成的可调用对象f和function类似,这个调用对象f也是不能用于find_if,因为他一定要传this进去,而getWhat本身就是一元谓词,f4直接变二元了。
 
 
 
 
 

以上是关于C++语言基础——函数(详解)的主要内容,如果未能解决你的问题,请参考以下文章

深入C++中API的问题详解_C 语言

详解C语言和C++最大的区别,附带新手学习建议

C++入门命名空间详解——解决的问题使用方法和C语言对比

extern "c"的作用详解

C语言函数详解(入门到进阶)

C语言正则表达式详解 regcomp() regexec() regfree()详解