文章目录
- 🍪统一列表初始化
- 🍪左值引用,右值引用
- 🥛概念和作用
- 🥛使用场景
- 🍪完美转发
- 🍪可变参数模板
C++11是C++的一次大更新,出现了很多实用的语法和特性,所以我们很有必要学习他的新东西
🍪统一列表初始化
C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加。
初始化列表是如何初始化对象的呢?答案是通过调用其构造函数。
以vector为例(其他容器都是一样的),看看底层实现
vector在C++11时,重载了一个新的构造函数,其中调用了initializer_List<T> il
- initializer_list 更多内容
是std里的一个类,只要调用{}来为容器对象初始化,都会调用先将这个类生成一个临时变量,然后在通过这个临时变量来构造出所目的对象。前提是容器里重载了initializer_list作为参数的构造函数。
🍪左值引用,右值引用
🥛概念和作用
了解左值引用,右值引用前,我们先了解左值和右值
左值和右值都是数据的表达式
- 左值:可以取地址+可以赋值
void test3()
{
// 以下的p、b、c、*p都是左值
int* p = new int(0);
int b = 1;
const int c = 2;
// 以下几个是对上面左值的左值引用
int*& rp = p;
int& rb = b;
const int& rc = c;
int& pvalue = *p;
}
- 右值:右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,不可以取地址
void test3()
{
double x = 1.1, y = 2.2;
// 以下几个都是常见的右值
10;
x + y;
fmin(x, y);
// 以下几个都是对右值的右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);
// 这里编译会报错:error C2106: “=”: 左操作数必须为左值
10 = 1;
x + y = 1;
fmin(x, y) = 1;
}
无论左值引用还是右值引用,都是给对象取别名
ps:右值是不可以取地址的,但是给取了别名后,会导致右值被存储到特定位置。且可以取到该位置的地址
- 总结
左值:
- 左值引用只能引用左值,不能引用右值。
- 但是const左值引用既可引用左值,也可引用右值
右值:
- 右值引用只能右值。
- 但是右值引用可以move以后的左值。
🥛使用场景
- 左值引用
优点:做参数和做返回值都可以提高效率。
缺点:但是当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回,只能传值返回。就势必会调用一次拷贝构造函数。
- 右值引用
优点:作为使用移动构造的形参,将临时变量的内容剪切过来,这样可以提高效率。
这里介绍一下移动拷贝和移动赋值。(以自己造的string为例)
可以看到,移动构造和赋值都是进行内容的转移。这里和库里实现的效果一致。
s1的内容被s3窃取了。
移动构造和赋值一般都是把快要被释放掉资源的临时变量的资源进行转移,从而减少开销。上图演示的例子是错误示范。
🍪完美转发
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(t);
//Fun(std::forward<T>(t)); // 完美转发
}
int test3()
{
PerfectForward(10); // 右值
int a;
PerfectForward(a); // 左值
PerfectForward(std::move(a)); // 右值
const int b = 8;
PerfectForward(b); // const 左值
PerfectForward(std::move(b)); // const 右值
return 0;
}
模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,
但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值,
我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用“完美转发”
std::forward 完美转发在传参的过程中保留对象原生类型属性,从而调用不同的函数,减少开销,提高性能。
🍪可变参数模板
template<class ...args>
void show(args...) // 显示args的参数列表个数
{
cout << sizeof...(args) << " ";
}
void print() {}
template<class T ,class ...args>
void print(T head, args ...rest)
{
cout << head << " ";
print(rest...);
}
void test3()
{
print(1, 2, 3.2, "x");
show(1, "2", 'x', 6);
}
可变参数通过函数递归来进行打印数据,由于这是编译时进行的,所以必须得重载个空函数来表示递归结束。
更多详细