💻文章目录
- 📄前言
- 右值引用
- 概念
- 右值引用的意义
- 移动构造和移动赋值
- 完美转发
- 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
移动构造和移动赋值
移动构造和移动赋值C++11为类新增的内置函数,在类传值返回的时候可以将类内资源转移到临时对象上,避免了深拷贝造成的资源浪费。
string(string&& s) //移动构造
{
cout << "string(string&& s) -- 移动拷贝" << endl;
swap(s);
}
//移动赋值
string& operator=(string&& s)
{
cout << "string& operator=(string&& s)-- 移动赋值" << endl;
swap(s);
return *this;
}
注意:最新的编译器会把优化开的比较高,不好用与学习移动语义
- gcc关闭优化:-fno-elide-constructors
完美转发
完美转发指的让数值保持其原本的属性,不因为传参而从右值转化成左值
void Fun(int &x){ cout << "左值引用" << endl; }
void Fun(const int &x){ cout << "const 左值引用" << endl; }void Fun(int &&x){ cout << "右值引用" << endl; }
void Fun(const int &&x){ cout << "const 右值引用" << endl; }
// 模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
// 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,
// 但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值,
// 我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的完美转发
template<typename T>
void PerfectForward(T&& t)
{
Fun(std::forward<T>(t)); //可以自己尝试如果不使用完美转发的结果。
}
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