当前位置: 首页 > 面试题库 >

请你详细介绍一下C++11中的可变参数模板、右值引用和lambda这几个新特性。

轩辕庆
2023-03-14
本文向大家介绍请你详细介绍一下C++11中的可变参数模板、右值引用和lambda这几个新特性。相关面试题,主要包含被问及请你详细介绍一下C++11中的可变参数模板、右值引用和lambda这几个新特性。时的应答技巧和注意事项,需要的朋友参考一下

可变参数模板:

C++11的可变参数模板,对参数进行了高度泛化,可以表示任意数目、任意类型的参数,其语法为:在class或typename后面带上省略号”。

例如:

Template<class ... T>
void func(T ... args)
{
cout<<”num is”<<sizeof ...(args)<<endl;
}

func();//args不含任何参数

func(1);//args包含一个int类型的实参

func(1,2.0)//args包含一个int一个double类型的实参

其中T叫做模板参数包,args叫做函数参数包

省略号作用如下:

1)声明一个包含0到任意个模板参数的参数包

2)在模板定义得右边,可以将参数包展成一个个独立的参数

C++11可以使用递归函数的方式展开参数包,获得可变参数的每个值。通过递归函数展开参数包,需要提供一个参数包展开的函数和一个递归终止函数。例如:

#include using namespace std;

// 最终递归函数

void print()

{

cout << "empty" << endl;

}

// 展开函数

template void print(T head, Args... args)
{
cout << head << ","; print(args...);
}
int main()
{
print(1, 2, 3, 4); return 0;
}

参数包Args ...在展开的过程中递归调用自己,没调用一次参数包中的参数就会少一个,直到所有参数都展开为止。当没有参数时就会调用非模板函数printf终止递归过程。

右值引用:

C++中,左值通常指可以取地址,有名字的值就是左值,而不能取地址,没有名字的就是右值。而在指C++11中,右值是由两个概念构成,将亡值和纯右值。纯右值是用于识别临时变量和一些不跟对象关联的值,比如1+3产生的临时变量值,2、true等,而将亡值通常是指具有转移语义的对象,比如返回右值引用T&&的函数返回值等。

C++11中,右值引用就是对一个右值进行引用的类型。由于右值通常不具有名字,所以我们一般只能通过右值表达式获得其引用,比如:

T && a=ReturnRvale();

假设ReturnRvalue()函数返回一个右值,那么上述语句声明了一个名为a的右值引用,其值等于ReturnRvalue函数返回的临时变量的值。

基于右值引用可以实现转移语义和完美转发新特性。

移动语义:

对于一个包含指针成员变量的类,由于编译器默认的拷贝构造函数都是浅拷贝,所有我们一般需要通过实现深拷贝的拷贝构造函数,为指针成员分配新的内存并进行内容拷贝,从而避免悬挂指针的问题。

但是如下列代码所示:

img

当类HasPtrMem包含一个成员函数GetTemp,其返回值类型是HasPtrMem,如果我们定义了深拷贝的拷贝构造函数,那么在调用该函数时需要调用两次拷贝构造函数。第一次是生成GetTemp函数返回时的临时变量,第二次是将该返回值赋值给main函数中的变量a。与此对应需要调用三次析构函数来释放内存。

而在上述过程中,使用临时变量构造a时会调用拷贝构造函数分配对内存,而临时对象在语句结束后会释放它所使用的堆内存。这样重复申请和释放内存,在申请内存较大时会严重影响性能。因此C++使用移动构造函数,从而保证使用临时对象构造a时不分配内存,从而提高性能

如下列代码所示,移动构造函数接收一个右值引用作为参数,使用右值引用的参数初始化其指针成员变量。

img

其原理就是使用在构造对象a时,使用h.d来初始化a,然后将临时对象h的成员变量d指向nullptr,从而保证临时变量析构时不会释放对内存。

img

完美转发:

完美转发是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另一个函数,即传入转发函数的是左值对象,目标函数就能获得左值对象,转发函数是右值对象,目标函数就能获得右值对象,而不产生额外的开销。

因此转发函数和目标函数参数一般采用引用类型,从而避免拷贝的开销。其次,由于目标函数可能需要能够既接受左值引用,又接受右值引用,所以考虑转发也需要兼容这两种类型。

C++11采用引用折叠的规则,结合新的模板推导规则实现完美转发。其引用折叠规则如下:

img

因此,我们将转发函数和目标函数的参数都设置为右值引用类型,

img

当传入一个X类型的左值引用时,转发函数将被实例为:

img

经过引用折叠,变为:

img

当传入一个X类型的右值引用时,转发函数将被实例为:

img

经过引用折叠,变为:

img

除此之外,还可以使用forward()函数来完成左值引用到右值引用的转换:

img

Lambda表达式:

Lambda表达式定义一个匿名函数,并且可以捕获一定范围内的变量,其定义如下:

capturemutable->return-type{statement}

其中,

[capture]:捕获列表,捕获上下文变量以供lambda使用。同时[]是lambda寅初复,编译器根据该符号来判断接下来代码是否是lambda函数。

(Params):参数列表,与普通函数的参数列表一致,如果不需要传递参数,则可以连通括号一起省略。

mutable是修饰符,默认情况下lambda函数总是一个const函数,Mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略。

->return-type:返回类型是返回值类型

{statement}:函数体,内容与普通函数一样,除了可以使用参数之外,还可以使用所捕获的变量。

Lambda表达式与普通函数最大的区别就是其可以通过捕获列表访问一些上下文中的数据。其形式如下:

img

Lambda的类型被定义为“闭包”的类,其通常用于STL库中,在某些场景下可用于简化仿函数的使用,同时Lambda作为局部函数,也会提高复杂代码的开发加速,轻松在函数内重用代码,无须费心设计接口。

 类似资料:
  • 在C++11之前,类模板和函数模板只能含有固定数量的模板参数。C++11增强了模板功能,允许模板定义中包含0到任意个模板参数,这就是可变参数模板。可变参数模板的加入使得C++11的功能变得更加强大,而由此也带来了许多神奇的用法。 可变参数模板 可变参数模板和普通模板的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在typename或class后面带上省略号...: template<ty

  • 本文向大家介绍Thinkphp中import的几个用法详细介绍,包括了Thinkphp中import的几个用法详细介绍的使用技巧和注意事项,需要的朋友参考一下 下面附上import的几个用法介绍 1、用法一 import('@.Test.Translate'); @,表示项目根目录。假定根目录是:App/ 导入类库的路径是:App/Lib/Test/Translate.class.php 结论:i

  • 本文向大家介绍请你介绍一下数据库的ACID特性相关面试题,主要包含被问及请你介绍一下数据库的ACID特性时的应答技巧和注意事项,需要的朋友参考一下 参考回答: 1)原子性:事务被视为不可分割的最小单元,事物的所有操作要不成功,要不失败回滚,而回滚可以通过日志来实现,日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作。 2)一致性:数据库在事务执行前后都保持一致性状态,在一致性状态下,所有

  • 本文向大家介绍请你介绍一下策略模式?相关面试题,主要包含被问及请你介绍一下策略模式?时的应答技巧和注意事项,需要的朋友参考一下 考察点:策略模式 策略模式也叫政策模式,是一种行为型设计模式,是一种比较简单的设计模式。策略模式采用了面向对象的继承和多态机制。略模式适合使用在:1.多个类只有在算法或行为上稍有不同的场景。2.算法需要自由切换的场景。3.需要屏蔽算法规则的场景。 使用策略模式当然也有需要

  • 本文向大家介绍你知道java8的新特性吗,请简单介绍一下相关面试题,主要包含被问及你知道java8的新特性吗,请简单介绍一下时的应答技巧和注意事项,需要的朋友参考一下 考察点:java8 Lambda 表达式 − Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中。 方法引用− 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合

  • 本文向大家介绍请你介绍一下C++中的智能指针?相关面试题,主要包含被问及请你介绍一下C++中的智能指针?时的应答技巧和注意事项,需要的朋友参考一下 智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。C++ 11中最常用的智能指针类型为shared_ptr,它采用引用计数的方法,记录当前内存资源被多少个智