技术开发 频道

Visual C++2010新特性:支持C++0x新标准

    【IT168 专稿】C++0x是C++编程语言预计的新标准的一个非正式的名称,今后会替换现有的C++标准,其中的X也会有特定的值。新版本中添加了语言核心内容和扩展的C++标准库。本文重点介绍在Visual C++ 2010中C++0x的语言特性,包含Lambda表达式、auto 关键字、static_assert 声明、右值(Rvalue)引用。

  Lambda 表达式

  很多编程语言都支持不具名函数,Lambda 表达式就是一种与不具名函数相关的编程技术。那什么是不具名函数呢?不具名函数就是只有函数体、而没有函数名的一种函数,如下图标红部分。

1
 

  在 for_each语句中,[](int n) { cout << n << " "; } 就是一个Lambda表达式,[] 操作符是 Lambda导引符, 它告诉编译器一个 Lambda表达式开始了; (int n) 是 Lambda参数声明,它告诉编译器不具名函数对象类的函数调用操作符带有哪些参数; { cout << n << " "; } 是复合声明 ,它是不具名函数对象类的函数调用操作符的函数体。不具名函数对象类的函数调用操作符默认返回 void 。

  Lambda 表达式的特性分别是简洁高效、自动推断返回类型。

  (1)简洁高效

  下面代码实现循环遍历输出vector中的每一个数。

1
 

  输出结果:

1
 

  上面两组代码实现相同的效果,通过比较我们就可以发现,Lambda表达式的语法更加简洁,使用起来更加简单高效。  

  (2) 自动推断返回类型

  Lambda 函数也并不总是必须返回void 。如果Lambda 的复合声明语句像是这样的 { return expression ; } ,那么Lambda 的返回类型就会自动地被推断成expression的类型。

1
 

  输出结果:

1

  图5

  在上述代码中,n*n*n是int类型,所以Lambda函数返回也是int类型。另外,如果是复杂复合语句的声明(如下图红色框标注部分),Lambda函数就不能自动推断出返回类型,需要您显式指定返回类型。下面代码示例出现复杂复合声明,用户指定Lambda函数返回类型。

1
 

  输出结果:

1
 

  上述代码中,有n*n*n是int类型,而n/2.0又是double类型,这样Lambda函数无法推断返回类型,所以需要开发人员指定返回类型。如果您忘记指定的话,会出现错误。如果去掉transform语句中的double,就会出现编译错误。

1
 

  另外,Lambda表达式分有状态和无状态两种。在前面的Lambda表达式中,[] 操作符是 Lambda导引符,标识表达式的开始,注意到[]中没有任何数据,这就是一个无状态的Lambda表达式,不包含数据成员。在Lambda导引符[]中指定capture-list的表达式,就是有状态的表达式,在[]中指定了x和y。

1  

  auto 关键字

  C++ 标准为 auto 关键字定义了初始和修订的含义。在 Visual C++ 2010 之前,该关键字在自动存储类中声明变量,即具有局部生存期的变量。在C++ 0x 中,该关键字从声明的初始化表达式中可以推导出变量的类型。也就是说,在一个声明中使用了auto关键字,意味着“让它和初始化它的事物具有相同的类型”。下面我们可以举例说明:

  (1)自动推断变量类型

1
 

  上面代码中,定义变量j为int类型,由于0是int类型,所以可以推断出使用auto 关键字声明的变量k也是int类型。

  (2)声明更简洁

1
 

  上述声明有着相同的效果,由于使用auto 关键字,使得第二行的声明更加简洁。

  另外,跟其他数据类型一样,我们也可以对auto关键字进行修饰,例如添加const,指针(*),左值引用(&),右值引用(&&)等等,编译器会根据auto类型所代表的真正的数据来决定这些修饰的具体含义。

  为了兼容一些旧有的C++代码,我们可以使用/Zc:auto[-](/Zc:auto:编译器从其初始化表达式中推导声明的变量的类型; /Zc:auto-:编译器将该变量分配给自动存储类)编译器选项,来告诉编译器是采用auto关键字的原有定义还是在新标准C++0x中的定义。具体设置见下图:

1
 

  static_assert 声明

  在新标准C++0x中,加入了对静态断言的支持,引入关键字static_assert来表示静态断言。使用静态断言,我们可以在程序的编译时期检测一些条件是否成立,这个特性在调试模板函数的模板参数时特别有用。在编译的时候,模板函数实例化,这时我们就可以使用静态断言去测试模板函数的参数是否按照我们的设计拥有合适的值。

1
 

  constant-expression:能够转换成Boolean类型的整数常量表达式,如果这个表达式返回值为False,使用参数string –literal显示编译失败的信息;否则的话,该静态断言声明没有生效。

  string-literal:用来显示编译失败的消息

1
 

  上面代码中标红的部分,N<2就是constant-expression,"Kitten requires N < 2。"就是string-literal。在编译的时候,我们在主函数中实例化Kitten这个结构体,static_assert声明会测试参数N的值,当N的值不小于2时就会产生一个断言错误,并将相应的调试帮助信息输出到“错误列表”窗口中。用“1”去实例化Kitten这个结构体时,因为1<2,返回True,静态断言声明没有生效。但是,使用“3” 实例化Kitten这个结构体时,因为3<2,返回False,此时就会产生断言错误,并输出错误信息。见下图

1
 

          static_assert声明是在编译时进行处理,所以不会产生任何运行空间和时间上的开销,比assert宏具有更好的效率。  

  右值(Rvalue)引用

  在新标准C++0x中,Rvalue 引用是一项重要的语言特性,我们可以通过操作符“&&”来声明一个右值(Rvalue)引用,原先在C++中使用“&”操作符声明的引用现在被称为左值(Lvalue)引用。左值引用和右值引用分别都是什么呢?左值引用是指那些单一表达式结束之后依然存在的持久对象,例如:obj,*ptr,prt[index],++x。右值引用是指那些表达式结束时(在分号处)就不复存在了的临时对象。例如:1729, x + y,std::string("meow"),x++。二者都是针对表达式而言,不是对象。其中,右值引用支持移动语义和完全转发,移动语义(Move Semantics)可以带来巨大的性能提升,而完全转发(Perfect Forwarding)可以让高度泛型代码的编写变的更容易。

  (1)移动语义

  拷贝的问题,助长了移动语义。拷贝是对源对象的复制操作,拷贝后的对象是独立存在的,这样对于源对象来说就是一种冗余,并且会有资源开销。尽管返回值优化(RVO) 和命名返回值优化(NRVO)在特定情况下可以对拷贝构造操作进行优化,这有助于减缓问题的严重性,但是它们不能够消除所有冗余的拷贝。下面是拷贝操作的示例:

右值(Rvalue)引用
 

  上面的代码,通过多次对象拷贝来实现了数据的传递,这样的拷贝操作,无疑会带来资源浪费和效率的低下,特别当这些对象很大的时候。如何能解决拷贝带来的问题呢?下面是通过右值引用实现和上面相同的功能。

右值(Rvalue)引用
 

  在这段重新实现的代码中,我们使用了一个move()函数来代替对象的赋值操作符“=”,move()只是简单地接受一个右值引用或者左值引用作为参数,然后直接返回相应对象的右值引用。这一过程不会产生拷贝(Copy)操作,而只会将源对象移动(Move)到目标对象。

  拷贝(Copy)操作会使源对象出现多份,而移动(Move)只是位置变化。移动语义正是通过右值引用,实现把拷贝操作变为移动对象,这样不仅节省了资源,同时还能提高性能。

  (2)完美转发

  完美转发是为解决早期版本中“转发”问题而提出的。在以前的C++标准中,如果编写泛型函数,如实现通过函数outer()把参数传递给函数inner()的功能,就需要根据参数的个数、参数的类型重载很多个函数。如下图:

右值(Rvalue)引用
 

  上面的代码是对outer()函数只包含一个参数、两种参数类型的重载。如果是多个参数、多种参数类型,那重载的函数就会更多了。“转发”问题不仅拖慢了编译速度,极大的影响了代码的可读性。在C++0x中,右值引用优雅的解决了这个问题。

  完美转发让你能简单而清晰地只写一个模板函数就可以转发所有的参数给任意函数,不管它带几个参数,也不管参数类型是什么。而且参数的非常量/常量, lvalue/rvalue 属性都能得以保留,让你可以像使用 inner() 一样使用 outer() 。如下图:

右值(Rvalue)引用
 

  在新标准C++0x中,使用右值引用,解决了在标准C++标准中无法解决的问题,提高了代码性能,使代码更加灵活、高效。

  小结

  在Visual C++ 2010 中,提供了对新标准C++0x的支持,本文从Lambda表达式、auto 关键字、static_assert 声明、右值(Rvalue)引用四个方面进行了详细介绍,这些特性增强了代码的简洁性,同时有助于提高代码效率和性能。不过这只是一个抛砖引玉的介绍,还有更多Visual C++ 2010在语言方面的新特性等待大家去发掘。

0
相关文章