1. static_assert 声明

static_assert 声明在编译时测试软件断言,这与在运行时进行测试的其他断言机制不同。 如果断言失败,则编译也将失败,且系统将发出指定的错误消息。

const int nValue = 3;

static_assert(nValue < 10, "Error");

这样编译时就会报出Error的错误提示信息。

  1. 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

  1. 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是int

auto和动态分配

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

  1. 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;});
  1. 右值引用

为了支持移动语义,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特性的更多相关文章

  1. ES6系列之项目中常用的新特性

    ES6系列之项目中常用的新特性 ES6常用特性 平时项目开发中灵活运用ES6+语法可以让开发者减少很多开发时间,提高工作效率.ES6版本提供了很多新的特性,接下来我列举项目中常用的ES6+的特性: l ...

  2. Java高级特性 第2节 java中常用的实用类(1)

    一.Java API Java API即Java应用程序编程接口,他是运行库的集合,预先定义了一些接口和类,程序员可以直接调用:此外也特指API的说明文档,也称帮助文档. Java中常用的包: jav ...

  3. 从effective C++中窥探C++11特性

    这几天在看effective C++3rd,这本书算是比较经典的一本入门C++的书了.虽然年代比较久远书中讲的好多模式已经被的新特性取代了,但是从这些旧的模式中可以了解到一些C++新特性设计的初衷,也 ...

  4. 【原】实时渲染中常用的几种Rendering Path

    [原]实时渲染中常用的几种Rendering Path 本文转载请注明出处 —— polobymulberry-博客园 本文为我的图形学大作业的论文部分,介绍了一些Rendering Path,比较简 ...

  5. C#中常用的读取xml的几种方法(转)

    本文完全来源于http://blog.csdn.net/tiemufeng1122/article/details/6723764,仅作个人学习之用. XML文件是一种常用的文件格式,例如WinFor ...

  6. 【Unity3d游戏开发】Unity3D中常用的物理学公式

    马三最近在一直负责Unity中的物理引擎这一块,众所周知,Unity内置了NVIDIA公司PhysX物理引擎.然而,马三一直觉得只会使用引擎而不去了解原理的程序猿不是一位老司机.所以对一些常用的物理学 ...

  7. php中常用的运算符

    运算符 运算符是告诉PHP做相关运算的标识符号. PHP运算符一般分为算术运算符.赋值运算符.比较运算符.三元运算符.逻辑运算符.字符串连接运算符.错误控制运算符. 1.变量名记得加“$” 符: 2. ...

  8. 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 ...

  9. 转载:每个C++开发者都应该使用的十个C++11特性

    这篇文章讨论了一系列所有开发者都应该学习和使用的C++11特性,在新的C++标准中,语言和标准库都加入了很多新属性,这篇文章只会介绍一些皮毛,然而,我相信有一些特征用法应该会成为C++开发者的日常用法 ...

随机推荐

  1. 直流电机驱动,TIMER口配置

    电机的电压输出能力和频率有关??? 修改前:------------------------------------------------------------------------------ ...

  2. Tokio,Rust异步编程实践之路

    缘起 在许多编程语言里,我们都非常乐于去研究在这个语言中所使用的异步网络编程的框架,比如说Python的 Gevent.asyncio,Nginx 和 OpenResty,Go 等,今年年初我开始接触 ...

  3. BZOJ054_移动玩具_KEY

    题目传送门 这道题我写IDA*写挂了,TLE+WA,只AC了两个点. 这道题标算BFS+状态压缩. code: /******************************************* ...

  4. [agc001E]BBQ Hard[组合数性质+dp]

    Description 传送门 Solution 题目简化后要求的实际上是$\sum _{i=1}^{n-1}\sum _{j=i+1}^{n}C^{A[i]+A[j]}_{A[i]+A[j]+B[i ...

  5. [BZOJ4444][SCOI2015]国旗计划-[ST表]

    Description 传送门 Solution 说真的这道题在场上没做出来的我必定是脑子有洞.. 我们用st表记录以某个位置开始,派了1<<j个战士能到达的最远位置. 由于边境线是一圈, ...

  6. 【BZOJ4803】逆欧拉函数

    [BZOJ4803]逆欧拉函数 题面 bzoj 题解 题目是给定你\(\varphi(n)\)要求前\(k\)小的\(n\). 设\(n=\prod_{i=1}^k{p_i}^{c_i}\) 则\(\ ...

  7. Retinex图像增强和暗通道去雾的关系及其在hdr色调恢复上的应用

    很多人都认为retinex和暗通道去雾是八杆子都打不着的增强算法.的确,二者的理论.计算方法都完全迥异,本人直接从二者的公式入手来简单说明一下,有些部分全凭臆想,不对之处大家一起讨论. 首先,为描述方 ...

  8. 【JUC源码解析】CountDownLatch

    简介 CountDownLatch,是一个同步器,允许一个或多个线程等待,直到一组操作在其他线程中完成. 概述 初始CountDownLatch时,会给定count,await方法会阻塞,直到coun ...

  9. centos7下python3与python2共存并且开启py3虚拟环境

    因为下载视频需要用到python3环境,今天在我的win上安装下载工具死活安装不上去,在大盘鸡上一下就安装成功了...可能在win上不兼容吧...无奈只能在大盘鸡上进行折腾了,顺便几个笔记 由于大盘鸡 ...

  10. Maven学习(八)-----Maven依赖机制

    Maven依赖机制 在 Maven 依赖机制的帮助下自动下载所有必需的依赖库,并保持版本升级. 案例分析 让我们看一个案例研究,以了解它是如何工作的.假设你想使用 Log4j 作为项目的日志.这里你要 ...