知识的学习在于点滴记录,坚持不懈
函数对象
重载了函数调用运算符()的类的对象,即为函数对象。
std::function 由上文可以看出:由于可调用对象的定义方式比较多,但是函数的调用方式较为类似,因此需要使用一个统一的方式保存可调用对象或者传递可调用对象。于是,std::function就诞生了。
std::function是一个可调用对象包装器,是一个类模板,可以容纳除了类成员函数指针之外的所有可调用对象,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟它们的执行。
std::function的使用用法:
#include <iostream>
#include <string>
#include <functional>
#include <map>
using namespace std;
void hello1()
{
cout << "hello world!" << endl;
}
void hello2(string str)
{
cout << str << endl;
}
int sum(int a, int b)
{
return a + b;
}
class Test
{
public: // 必须依赖一个对象
void hello(string str)
{
cout << str << endl;
}
};
int main()
{
/*
1.用函数类型实例化function
2.通过function调用operator()函数的时候,需要根据函数类型传入相应的参数
*/
// 从function的类模范定义处,看到希望用一个函数类型实例化function
function<void()> func1(hello1);
func1(); // func1.operator() -> hello1()
function<void(string)> func2(hello2);
func2("hello2"); // func2.operator()(string str) -> hello2(str)
function<int(int, int)> func3 = sum;
cout << func3(20, 30) << endl;
function<int(int, int)> func4 = [](int a, int b)->int {return a + b; };
cout << func4(100, 200) << endl;
function<void(Test*, string)> func5 = &Test::hello;
func5(&Test(), "call Test::hello");
}
1.用函数类型实例化function。
2.通过function调用operator()函数的时候,需要根据函数类型传入相应的参数。
我们知道了function类型怎么使用了之后,来了解了解它的原理是什么????我们可以自己来实现一个myfunction类型:
#include <iostream>
#include <functional>
#include <string>
using namespace std;
void hello(string str){ cout << str << endl;}
int sum(int a, int b)
{
return a + b;
}
template<typename Fty>
class myfunction{};
template<typename R, typename A1>
class myfunction<R(A1)>
{
public:
using PFUNC = R(*)(A1);
myfunction(PFUNC pfunc) :_pfunc(pfunc){}
R operator()(A1 arg)
{
return _pfunc(arg);
}
private:
PFUNC _pfunc;
};
我们先提供了一个类模板,然后使用using定义了一个类型为PFUNC为函数的类型,给出构造函数,传入的参数就是函数的函数名,重载了()运算符,传入了参数,最后调用hello函数,实现了function类,但是这个myfunction类型只能实现这一个函数传入,我们不能每写一个函数就定义一个function类型,C++11给我们提供了这样的功能,模板的全部特例化,所以,我们只需要写一个类来代表所有的类型。
// function函数对象类型的实现原理
#include <iostream>
#include <functional>
#include <string>
using namespace std;
void hello(string str){ cout << str << endl;}
int sum(int a, int b)
{
return a + b;
}
template<typename Fty>
class myfunction{};
template<typename R, typename... A>
class myfunction<R(A...)>
{
public:
using PFUNC = R(*)(A...);
myfunction(PFUNC pfunc) :_pfunc(pfunc) {}
R operator()(A... arg)
{
return _pfunc(arg...);
}
private:
PFUNC _pfunc;
};
int main()
{
myfunction<void(string)> func1(hello);
func1("hello world!");
//myfunction<int(int, int)> func2(sum);
return 0;
}
什么是模板的完全特例化和非完全(部分)特例化??
template<typename T>
bool compare(T a, T b)
{
return a > b;
}
int main()
{
compare(23, 30);
return 0;
}
根据这段代码,我们根据模板的实参推演,推演出形参的类型T是一个int类型,然后它会从模板函数中实例化出来一个关于int类型的函数出来进行编译。
而我们要比较两个字符的大小,我们需要特例化一个函数:
template<typename T>
bool compare(T a, T b)
{
cout << "T" << endl;
return a > b;
}
template<>
bool compare<const char*>(const char* a, const char* b)
{
cout << "const char*" << endl;
return strcmp(a, b);
}
int main()
{
compare(23, 30);
compare("aaa", "bbb");
return 0;
}
它的运行结果为:
这个就叫做完全特例化,因为这个T是完全已知的,就是const char* 类型的。也就是说明,这个模板只针对const char *类型特例化出来了一个模板实例化。
有可能,我们对指针类型的类的构造会有改变,我们不知道它是什么指针,只知道它是一个指针类型的,我们则可以这特例化,对指针的类型进行部分特例化。
下面这个是对指针类型提供的部分特例化版本:
template<typename Ty>
class vector<Ty*>
{
public:
vector() { cout << "call vector:: <Ty*> init" << endl; }
private:
};
int main()
{
vector<int> vec1;
vector<char*> vec2;
vector<int*> vec3;
}
所以,有完全特例化的它就匹配完全特例化进行实例化,没有就寻找部分特例化进行实例化,都没有那么它就使用原模版进行实例化。
Lambda表达式
Lambda有很多叫法,有Lambda表达式、Lambda函数、匿名函数,本文中为了方便表述统一用Lambda表达式进行叙述。 ISO C++标准官网展示了一个简单的lambda表示式实例:
#include <algorithm>
#include <cmath>
void abssort(float* x, unsigned n) {
std::sort(x, x + n,
// Lambda expression begins
[](float a, float b) {
return (std::abs(a) < std::abs(b));
} // end of lambda expression
);
}
在上面的实例中std::sort
函数第三个参数应该是传递一个排序规则的函数,但是这个实例中直接将排序函数的实现写在应该传递函数的位置,省去了定义排序函数的过程,对于这种不需要复用,且短小的函数,直接传递函数体可以增加代码的可读性。
lambda表达式语法
[capture-list] : 捕捉列表 ,该列表总是出现在 lambda 函数的开始位置, 编译器根据 [] 来 判断接下来的代码是否为 lambda 函数 , 捕捉列表能够捕捉上下文中的变量供 lambda 函数使用 。 (parameters) :参数列表。与 普通函数的参数列表一致 ,如果不需要参数传递,则可以 连同 () 一起省略 mutable :默认情况下, lambda 函数总是一个 const 函数, mutable 可以取消其常量 性。使用该修饰符时,参数列表不可省略 ( 即使参数为空 ) 。 ->returntype :返回值类型 。用 追踪返回类型形式声明函数的返回值类型 ,没有返回 值时此部分可省略。 返回值类型明确情况下,也可省略,由编译器对返回类型进行推 导 。 {statement} :函数体 。在该函数体内,除了可以使用其参数外,还可以使用所有捕获 到的变量。 注意: 在lambda 函数定义中, 参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为 空 。因此 C++11 中 最简单的 lambda 函数为: []{} ; 该 lambda 函数不能做任何事情。 捕获列表说明 捕捉列表描述了上下文中那些数据可以被lambda 使用 ,以及 使用的方式传值还是传引用 。 [var] :表示值传递方式捕捉变量 var [=] :表示值传递方式捕获所有父作用域中的变量 ( 包括 this) [&var] :表示引用传递捕捉变量 var [&] :表示引用传递捕捉所有父作用域中的变量 ( 包括 this) [this] :表示值传递方式捕捉当前的 this 指针 注意: a. 父作用域指包含 lambda 函数的语句块 b. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割 。 比如:[=, &a, &b] :以引用传递的方式捕捉变量 a 和 b ,值传递方式捕捉其他所有变量 。 [&,a, this] :值传递方式捕捉变量 a 和 this ,引用方式捕捉其他变量 c. 捕捉列表不允许变量重复传递,否则就会导致编译错误 。 比如:[=, a] : = 已经以值传递方式捕捉了所有变量,捕捉 a 重复。 d. 在块作用域以外的 lambda 函数捕捉列表必须为空 。 e. 在块作用域中的 lambda 函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者 非局部变量都会导致编译报错。 f. lambda 表达式之间不能相互赋值 ,即使看起来类型相同。
template<typename T>
class TestLambda
{
public:
TestLambda() {}
void operator()()
{
cout << "hello world!" << endl;
}
};
int main()
{
auto a = []() -> void {cout << "hello world!" << endl; };
a();
TestLambda<int> t1;
t1();
return 0;
}
看上面的代码,是一个函数对象,重载()运算符。Lambda表达式中,[]中的内容就相当于TestLambda类中构造函数的参数列表,()就相当于类中()重载函数的传入的参数,返回类型就是()重载函数的返回类型,函数体就是()重载函数里面的函数体。
如果Lambda表达式的返回值不需要,指向符和返回值是可以省略的。
[]:表示不捕获任何外部变量。
[=]:以传值的方式捕获外部的所有变量。
[&]:以传引用的方式捕获外部的所有变量。
[this]:捕获外部的this指针。
[=,&a]:以传值的方式捕获外部的所有变量,但是a变量以传引用的方式捕获。
[a,b]:以传值的方式捕获外部变量a和b。
[a, &b]:a以值传递捕获,b以传引用的方式捕获。
template<typename T>
class TestLambda
{
public:
TestLambda() {}
void operator()()
{
cout << "hello world!" << endl;
}
};
int main()
{
auto func = []() -> void {cout << "hello world!" << endl; };
func();
TestLambda<int> t1;
t1();
auto func1 = [](int a, int b) -> int {return a + b; };
cout <<func1(10, 20) << endl;
int a = 10;
int b = 20;
auto func3 = [&a, &b]()mutable->void
{
int temp = a;
a = b;
b = temp;
};
func3();
cout << a << b << endl;
return 0;
}
operator()在底层默认是一个常方法,即就是在函数体内不能修改成员变量的值,所以我们在交换值的时候,需要在()后加上一个mutable,表示可以修改值,或者是我们不使用按值传递,我们使用按引用传递,传入a,b的引用,这样就能交换a,b的值啦!
Lambda表达式的使用
int main()
{
vector<int> vec;
for (int i = 0; i < 20; i++)
{
vec.push_back(rand() % 100 + 1);
}
sort(vec.begin(), vec.end(), [](int a, int b)->bool {return a > b; });
for (int v : vec)
{
cout << v << " ";
}
cout << endl;
// 65按顺序插入序列,找第一个小于65的数字的位置
auto it = find_if(vec.begin(), vec.end(), [](int a)->bool { return a < 65; });
if (it != vec.end())
{
vec.insert(it, 65);
}
for (int v : vec)
{
cout << v << " ";
}
cout << endl;
for_each(vec.begin(), vec.end(), [](int a){
if(a%2==0)
cout << a << " ";
});
return 0;
}
Lambda表达式只能使用在语句当中,如果想跨语句使用之前定义好的lambda表达式,应该怎么办??用什么类型来表示lambda表达式???
lambda表达式是一个函数对象,用function来进行表示。
标签:11,cout,int,C++,捕捉,类型,lambda,函数 From: https://blog.csdn.net/m0_73839343/article/details/143605814