VSVC2010中常用的C++11特性
static_assert 声明
static_assert 声明在编译时测试软件断言,这与在运行时进行测试的其他断言机制不同。 如果断言失败,则编译也将失败,且系统将发出指定的错误消息。
const int nValue = 3; static_assert(nValue < 10, "Error");这样编译时就会报出Error的错误提示信息。
decltype作为操作符
用于返回表达式的数据类型。
int Add(int a, int b) { return a+b; } double dVal = 0.1; const double dVal0 = 0.2; const double& dVal1 = &dVal; decltype(dVal) dVal2 = 0.3; // dVal2是double类型 decltype(0.2) dVal3 = 0.2; // dVal3是double类型 decltype(dVal1) dVal4 = &dVal0; // dVal4是const double&类型 decltype(Add(, )) var = ; // var是函数返回的类型即int类型 decltype((dVal)) dVal5 = &dVal; // decltype((variable))多括号结果永远是引用 int odd[] = {, , , , }; int even[] = {, , , , }; typedef int ArrType[]; ArrType *Fun1(int i) { return (i % ) ? &odd : &even; } decltype(odd) *Fun2(int i) { return (i % ) ? &odd : &even; } ArrType *arr1 = Fun1(); ArrType *arr2 = Fun2();
Fun2使用decltype表示它的返回类型是个指针,并且该指针类型与odd类型一致。因为odd是数组,所以Fun2返回一个指定5个整数的数组的指针。因为decltype(odd)类型的结果是数组,所以如果想Fun2返回指针则必须在函数声明时加上*符号。
使用尾置返回类型
尾置返回类型(trailing return type)是在形参列表后面以->符号开始标明函数的返回类型,并在函数返回类型处用auto代替。尾置返回类型即可以直接指明类型,也可以用decltype推出出类型。形式:
auto Function(int i)->int
auto Fun3(int i)->int(*)[] // 返回指定数组的指针
int n = ;
auto Function(int i)->decltype(n)
template<class T, class W>
auto Function(T t, W w)->decltype(t+w)
{
return t +w;
}
// 如果是自定义类型,则应该重载+实现t+w
注:C++14中,已经将尾置返回类型去掉了,可以直接用auto推导出类型。
参考:msdn.microsoft.com/en-us/library/dd537655(v=vs.100).aspx
auto关键字
c++11修改了auto关键字的作用,auto类型说明符能让编译器替我们去分析表达式的初始化值来推导出变量的类型。
类型推导
int j = ; auto m = j; // m是int类型 auto n = ; // 0默认是int类型 map<int,list<string>>::iterator i = m.begin(); auto i = m.begin(); const int* const pInt = new int(); auto *pAuto = pInt; // pAuto为int* cosnt类型,忽略顶层const保留底层const *pAuto = ; // Error pAuto = new int(); // OK const auto& ref = ; // 0K int Add(int a, int b){return a+b;} auto ret = Add(, ); // ret是intauto和动态分配
auto pInt1 = new int(); int nValue = ; auto pInt2 = new auto(nValue); //以nValue类型动态申请内存并以nValue赋值 delete pInt2; // 以下是申请指向&nValue的指针 auto** ppInt = new auto(&nValue); int** ppInt = new int*; *ppInt = &nValue; delete ppInt;注:VS2010中对auto的动态分配支持有Bug,delete时会报错,所以VS2010中不允许使用此功能。
参考:https://msdn.microsoft.com/en-us/library/dd293667(v=vs.100).aspx
Lambda表达式
Lambda表达式就是匿名函数。Lambda表达式表示一个可调用的代码单元。与其他函数一样,Lambda具有一个返回类型、一个参数列表和一个参数体。一个Lambda表达式具有如下形式:
[capture list](parameter list))->return type { function body}
Capture list(捕获列表)Lambda函数中定义的局部变量列表,可以为空。
Parameter list、function body和普通函数一样。
return type,尾置类型指明,一般情况可以没有,编译器会自动推导出返回类型。当函数体有多个返回值时,编译会产生错误,需要指明返回类型。
string strRet = [](const string& str) { return "Hello from " + str; }("Lambda"); auto fun = [](const string& str)->string { return "Hello from " + str; }; strRet = fun("Lambda");Lambda表达式可以作为函数参数,例如在算法函数中调用时:
int arr[] = {}; generate(arr, arr+, []()->int { return rand() % ; });捕获列表的使用,指出数组第1个大于10的元素.
int nFlag = ; int nArr[] = {, , , , , }; auto first = find_if(nArr, nArr+, [nFlag](int nValue){return nValue > nFlag;});
右值引用
为了支持移动语义,C++11引入了新的引用类型——左值引用(RValue Reference)。右值引用,即绑定到右值的引用,通过&&来获取右值的引用。
左值右值
左值:有具体的名字,作用域不止当前语句。
右值:匿名、作用域仅在当前语句。
C++11里面对此作出的定义是:Things that are declared as rvalue reference can be lvalues or rvalues. The distinguishing criterion is: if it has a name, then it is an lvalue. Otherwise, it is an rvalue.
普通类型的常量都是左值,但是字符串常量因为生存周期是全局的,所以字符串常量是左值。
int&& nRRef = 1;
const string& strLRef = “LValue Reference”;
// nRRef虽然是左值引用,但它是具名的,所以nRRef是左值
右值引用
右值一旦离开当前语句,其生存期就会被销毁。而右值引用则将右值的有效性移动到右值引用这个左值上。
通过右值引用的定义可以看出它的主要作用是将临时变量的生存周期给转移了,这样就减少创建变量销毁对象的损耗。
构造函数和赋值函数是创建对象最常用函数,也是右值引用发挥作用的地方。
移动构造函数和移动赋值函数
class CMyString { public: CMyString() { m_data = NULL; m_len = ; } CMyString(const char* p) { m_len = strlen (p); Init(p); } CMyString(const CMyString&& str) { m_len = str.m_len; m_data = str.m_data; str.m_data = NULL; std::cout << "Copy Constructor is called! source: " << m_data << std::endl; } CMyString& operator=(const CMyString&& str) { if (this != &str) { m_len = str.m_len; m_data = str.m_data; str.m_data = NULL; } std::cout << "Copy Assignment is called! source: " << m_data << std::endl; return *this; } virtual ~CMyString() { if (m_data) delete[] m_data; } private: void Init(const char *s) { m_data = new char[m_len+]; memcpy(m_data, s, m_len); m_data[m_len] = '\0'; } private: char* m_data; size_t m_len; }; CMyString GetMyString() { CMyString str = "abc"; return str; // A } int _tmain(int argc, _TCHAR* argv[]) { CMyString myStr; myStr = GetMyString(); // B:1个右值赋给1个左值 return ; }代码A返回1个无名的临时对象,其实就是返回1个右值,这里就会调用右值构造函数.代码B将返回的右值赋给左值,同样调用右值赋值函数即移动赋值函数。
注:一旦资源完成移动(赋值)之后,源对象(即右值引用对象)不能再指向被移动的资源。正如上面的移动构造函数及移动赋值函数在完成指针转移时,右值引用的指针必须指向NULL。
标准库函数 std::move
移动构造函数和移动赋值函数只接受右值作为参数,而所有的具名对象都是左值。那么能不能将左值当作右值来使用呢?答案当然是可以的。标准库提供了std::move这个函数,它完成的作用只是一个类型转换,即将左值类型转换成右值引用类型。move函数是通过模板实现的,VS2010代码如下:
// TEMPLATE _Remove_reference template<class _Ty> struct _Remove_reference { // remove reference typedef _Ty _Type; }; template<class _Ty> struct _Remove_reference<_Ty&> { // remove reference typedef _Ty _Type; }; template<class _Ty> struct _Remove_reference<_Ty&&> { // remove rvalue reference typedef _Ty _Type; }; // TEMPLATE FUNCTION move template<class _Ty> inline typename tr1::_Remove_reference<_Ty>::_Type&& move(_Ty&& _Arg) { // forward _Arg as movable return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg); }上面这段代码很简单,却有点难懂。其实这里利用的是技术,也即利用了模板特化和偏特化。我们可以看一段简单的示例来理解这段代码的功能(详细见我之前文章)
template< typename T > struct STRUCT_TYPE { typedef int MY_TYPE; typedef __int64 POWER_TYPE; }; template<> struct STRUCT_TYPE<double> { typedef float MY_TYPE; typedef double POWER_TYPE; }; template< typename T > struct STRUCT_ALGO { // 下面的Typename是指示T::MY_TYPE是一个类型而不是成员变量 typedef typename STRUCT_TYPE<T>::MY_TYPE myType; typedef typename STRUCT_TYPE<T>::POWER_TYPE powType; powType GetPow(const myType& value) { return value*value; } }; int _tmain(int argc, _TCHAR* argv[]) { __int64 nPow = STRUCT_ALGO<int>::GetPow(); double dPow = STRUCT_ALGO<double>::GetPow(5.0); return ; }通过上面的代码可以看出,通过不同的模板形参,调用不同模板里面的typedef类型,这样可以达到不同类型模板形参数使用相同typedef名,共用代码。
我们再来看move函数的实现,其实也是一样的。
一开始创建通胀模板_Remove_reference,然后利用模板偏特化,这里只部分特化左值引用和右值引用特性。所以模板形参不能为空,如果是全特化的参数则省略。
首先了解下引用叠加的规则:
X& &、X&& &、X& &&均叠加成X&
X&& &&则叠加成X&&
string str1 = “abc”; string&& str2 = std::move(string(”abc”)); string&& str3 = std::move(str1); string&& str4 = std::move(str3);string(“abc”)实参明显是一个右值,则调用通用模板。
str1是1个左值,move(&&)函数则会将类型推导为左值引用。
str3则是1个右值引用,则move函数将会将其类型推导为右值引用。
_Remove_reference就达到去除引用的作用。
简单理解就是move(&&)可以接受所有类型的参数,无论是左值、右值、左值引用、右值引用均可。
(typename tr1::_Remove_reference<_Ty>::_Type&&)则是去掉引用,然后强制类型转换成右值引用。所有类型都可以强制转换成右值引用。
标准库函数 std::forward
有些函数需要将其实参连同类型不变地转发给其他函数,包括实参的是否cosnt、
左值还是右值。函数std::forward就是为了完成这个功能而创建的。
forward在VS2010中的实现如下:
// TEMPLATE CLASS identity template<class _Ty> struct identity { // map _Ty to type unchanged typedef _Ty type; const _Ty& operator()(const _Ty& _Left) const { // apply identity operator to operand return (_Left); } }; // TEMPLATE FUNCTION forward template<class _Ty> inline _Ty&& forward(typename identity<_Ty>::type& _Arg) { // forward _Arg, given explicitly specified type parameter return ((_Ty&&)_Arg); }如果一个函数参数是指向模板类型参数的右值引用(如T&&),它对应的const属性、左值、右值属于将得到保持。
void Add(const int& i) { cout << "inner(const X&)" << endl; } void Add(int& i) { i++; cout << "inner(const X&)" << endl; } void Add(int&& i) { cout << "inner(X&&)" << endl; } template<typename T> void AllAdd(T&& t) { Add(t); // 调用1 Add(forward<T>(t)); // 调用2 } int _tmain(int argc, _TCHAR* argv[]) { int nTemp = ; const int nValue = nTemp; AddAll(nValue); // 1, 2调用是相同的,均调用void Add(const int& i) AddAll(nTemp); // 1, 2调用是相同的,均调用void Add(int& i) AddAll(int()); // 1调用的是void Add(int& i),因为形参t有名字,是左值,故优先调用相应引用 // 2调用void Add(int&& i),因为forward<T>能够推导出T是右值引用 return ; }通过上面的示例就能够很好的理解std::forward所谓的完美转发了,因为只要是通过T&&的形参,forward<T>都能够返回它的实际类型.
VSVC2010中常用的C++11特性的更多相关文章
- ES6系列之项目中常用的新特性
ES6系列之项目中常用的新特性 ES6常用特性 平时项目开发中灵活运用ES6+语法可以让开发者减少很多开发时间,提高工作效率.ES6版本提供了很多新的特性,接下来我列举项目中常用的ES6+的特性: l ...
- Java高级特性 第2节 java中常用的实用类(1)
一.Java API Java API即Java应用程序编程接口,他是运行库的集合,预先定义了一些接口和类,程序员可以直接调用:此外也特指API的说明文档,也称帮助文档. Java中常用的包: jav ...
- 从effective C++中窥探C++11特性
这几天在看effective C++3rd,这本书算是比较经典的一本入门C++的书了.虽然年代比较久远书中讲的好多模式已经被的新特性取代了,但是从这些旧的模式中可以了解到一些C++新特性设计的初衷,也 ...
- 【原】实时渲染中常用的几种Rendering Path
[原]实时渲染中常用的几种Rendering Path 本文转载请注明出处 —— polobymulberry-博客园 本文为我的图形学大作业的论文部分,介绍了一些Rendering Path,比较简 ...
- C#中常用的读取xml的几种方法(转)
本文完全来源于http://blog.csdn.net/tiemufeng1122/article/details/6723764,仅作个人学习之用. XML文件是一种常用的文件格式,例如WinFor ...
- 【Unity3d游戏开发】Unity3D中常用的物理学公式
马三最近在一直负责Unity中的物理引擎这一块,众所周知,Unity内置了NVIDIA公司PhysX物理引擎.然而,马三一直觉得只会使用引擎而不去了解原理的程序猿不是一位老司机.所以对一些常用的物理学 ...
- php中常用的运算符
运算符 运算符是告诉PHP做相关运算的标识符号. PHP运算符一般分为算术运算符.赋值运算符.比较运算符.三元运算符.逻辑运算符.字符串连接运算符.错误控制运算符. 1.变量名记得加“$” 符: 2. ...
- SSH框架应用中常用Jar包用途介绍
struts2需要的几个jar包:1)xwork-core-2.1.62)struts2-core-2.1.83)ognl-2.7.34)freemarker-2.3.155)commons-io-1 ...
- 转载:每个C++开发者都应该使用的十个C++11特性
这篇文章讨论了一系列所有开发者都应该学习和使用的C++11特性,在新的C++标准中,语言和标准库都加入了很多新属性,这篇文章只会介绍一些皮毛,然而,我相信有一些特征用法应该会成为C++开发者的日常用法 ...
随机推荐
- ubuntu18.04 VirtualBox 开启虚拟机出错 Kernel driver not installed (rc=-1908)
写的很明白了 提示缺少GCC PERL MAKE,安装 重试..... 重启VM 搞定....
- 无法获得锁 /var/lib/apt/lists/lock - open (11 资源临时不可用)
具体如下: 1.ps-aux 查出apt-get进程的PID,通常是一个四位数字. 2.用sudo kill PID代码 杀死进程 3.用sudo apt-get update,sudo apt-ge ...
- POJ2513_Colored Sticks_KEY
题目传送门 题目大意:给你若干根木棍,每根木棍有前后两种颜色,连接两根木棍需要前后颜色相同,求能否将所有木棍连接在一起. Solution: 不要将木棍看成点,将颜色看成点. 其实就是求是否存在欧拉路 ...
- [2016北京集训试题8]连在一起的幻想乡[dp+无向图计数]
Description Solution 本博客参考yww大佬的博客,为了加深理解我就自己再写一遍啦. 以下的“无向图”均无重边无自环. 定义f0[n]为n个点构成的无向图个数,f1[n]为n个点构成 ...
- 成都Uber优步司机奖励政策(4月10日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- 1563: [NOI2009]诗人小G
1563: [NOI2009]诗人小G https://lydsy.com/JudgeOnline/problem.php?id=1563 分析: 直接转移f[i]=f[j]+cost(i,j),co ...
- dsp6657的helloworld例程测试-第二篇-CFG文件
1. 上一篇疑问,int StackTest()这个函数是怎么运行的,后来在.cfg文件找到了答案,.cfg包含丰富的信息,对于用惯C语言的,确实不太习惯 var Memory = xdc.useMo ...
- 关于nginx反向代理504 gateway time-out配置
问题描述: 使用nginx的默认配置用作后端处理服务的反向代理,针对处理时间超过1分钟的请求,返回504 gateway time-out,但后端服务还在执行中. 原因分析: nginx代理默认超时时 ...
- javaweb(八)——HttpServletResponse对象(二)
一.HttpServletResponse常见应用——生成验证码 1.1.生成随机图片用作验证码 生成图片主要用到了一个BufferedImage类, 生成随机图片范例: 1 package gacl ...
- KEIL5的安装
安装注意事项 1.最好不要安装在带有中文路径的文件夹. 2.试用版的Keil MDK只能编译32K以下的代码,代码大于32K只能使用正版或破解版才能编译通过. 安装MKD 这里选择MKD512A版本安 ...