
💻文章目录
- 📄前言
- 右值引用
- 概念
- 右值引用的意义
- lambada表达式
- 包装器
- function包装器
- bind包装器
- 📓总结
📄前言
C++标准10年磨一剑,于2011年迎来了它真正意义上的第二个标准,C++11能更好地适用与系统开发和库开发,语法与更加的繁华与简单化,本篇文章将重点介绍其中的右值引用、lambda表达式、包装器。
右值引用
概念
要讨论什么是右值引用前,得要分清什么是左值,什么是右值。
左值一般为位变量名或解引用的指针,左值可以出现在赋值符号的左边,也可以出现在赋值符号的右边。
右值一般位常量,表达式返回值,函数返回值,右值可以出现在赋值符号的右边,但不能出现在符号的左边,且右值不能取地址 ,右值引用就是对右值的引用,起别名。
下面的左值和右值的对比
int main()
{
/*左值*/
int i = 0, j = 0;
const int n = 123;
const char* str = "abcd";
/*左值引用*/
int& ii = i;
const char*& rstr = str;
const int& nn = n;
/*右值*/
10;
i + n;
max(n, j);
/*右值引用*/
int&& rri = 12;
double&& rr2 = 12.12 / 1.1;
return 0;
}
右值引用的意义
左值引用其最大用途就是可以不用拷贝参数直接将数值传给函数,从而我们再也不需要担心传值拷贝的效率低下了。
注意:move函数可以使左值强行转化成右值
vector<int>&& test(vector<int>&& nums) //这里右值变成了左值
{
printf("%p\n", &nums);
sort(nums.begin(), nums.end(), greater<int>());
return std::move(nums); //转化成右值
}
int main()
{
vector<int> nums {1,2,3,4,5,6,7,8,9};
printf("%p\n", &nums);
test(std::move(nums)); //强制转化成右值
printf("%p\n", &nums);
return 0;
}
结果:
0x7ffc04e86810
0x7ffc04e86810
0x7ffc04e86810
lambada表达式
lambda的书写格式[捕捉列表] (参数列表) mutable -> return-type { 函数体 }
1.[捕捉列表]:捕捉列表可以捕捉上下文的变量供lambda函数使用,使用 = 符号表示捕捉的变量都是传值传递,& 表示都是引用传递,并且编译器根据[ ]来判断接下来的代码是否为lambda函数。
2. (参数列表):与普通函数的参数列表一样。
3. 普通情况下,lambda函数都是一个const函数,mutable可以取消器常量性
4. {函数体}:在函数体内可以使用参数列表或捕捉列表的变量。
5. return-type:lambda表达式会自动推导返回值,所以返回类型可写可不写
样例
int main()
{
vector<int> nums{ 1,2,3,4,5 };
sort(nums.begin(), nums.end(), [&](int& a, int& b) { return a > b; });
int a = 3, b = 3; //lambda表达式也可以使用auto表达式来定义。
auto add = [](int a, int b) { return a + b; };
int x = add(a, b);
return 0;
}
包装器
function包装器
function包装器是用于解决函数模板可调用的类型太多,而导致的效率低下问题。
template<class F, class T>
T useF(F f, T x)
{
static int count = 0;
cout << "count:" << ++count << endl;
cout << "count:" << &count << endl;
return f(x);
}
double f(double i)
{
return i / 2;
}
struct Functor
{
double operator()(double d)
{
return d / 3;
}
};
int main()
{
// 函数名
cout << useF(f, 11.11) << endl;
// 函数对象
cout << useF(Functor(), 11.11) << endl;
// lamber表达式
cout << useF([](double d)->double{ return d/4; }, 11.11) << endl;
return 0;
}
允许上面程序会发现useF函数模板实例化了三份,而包装器就可以解决这个问题。
int main()
{
// 函数名(函数指针)
std::function<int(int, int)> func1 = useF;
// 函数对象
std::function<int(int, int)> func2 = Functor;
// lamber表达式
std::function<int(int, int)> func3 = [](const int a, const int b)
cout << func1(1, 2) << endl;
cout << func2(1, 2) << endl;
cout << func3(1, 2) << endl;
return 0;
}
bind包装器
bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。
bind原型
// 原型如下:
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2)
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
bind可以调整函数的参数顺序
int func(int a, int b)
{
cout << "a: " << a << endl;
cout << "b: " << b << endl;
return a / b;
}
int main()
{
//_1 与 _2 是参数的顺序,在命名空间placeholders中定义。
auto func1 = bind(func, placeholders::_2, placeholders::_1);
cout << func1(5, 10) << endl;
}
给函数参数默认值
int func(int a, int b)
{
cout << "a: " << a << endl;
cout << "b: " << b << endl;
return a / b;
}
int main()
{
//_1 与 _2 是参数的顺序,在命名空间placeholders中定义。
auto func1 = bind(func, 13, 6);
cout << func1(5, 10) << endl; //给定默认值后便不可修改。
function<int(int, int)> func2 = bind(func, placeholders::_2, placeholders::_1); //也可以和function一起使用
}
📓总结
📜博客主页:主页
📫我的专栏:C++
📱我的github:github
