首页 > 代码库 > C++11简介(1)

C++11简介(1)

原文地址:http://www.codeproject.com/Articles/312029/Cplusplus-A-Glance-part-of-n

1、介绍

C++0x现在是一个正式标准了,被称作C++11。ISO C++在2011年正式通过这个标准。这篇文章的目的是浏览C++11的特性和介绍VS2010中支持的特性,且可以作为你研究个别特性的一个开端。
因为长度原因,文章被分为了几部分。我自己也十分害怕一次性阅读那么长的文档,再说一次把C++11特性写完将会是工作变得无聊。
这是我在codeproject的第一篇文章,如果有格式或其他错误请包涵。

2、背景

在Charles Babbage发明了差分机后,过了近一个世纪才进入电子计算机时代。在1940s,由于电子计算机处理能力和内存的限制,只有汇编语言。在过了十多年后,1950到1970之间,时代见证了许多编程语言的兴起,其中有些至今还被使用。
在1979年,Bjarne Stroustrup在贝尔实验室工作,他的工作时加强C语言,首先增加了类,然后增加了虚函数、运算符重载、多继承、模板异常处理。他把加强的C言语叫做“带类的C语言”。1983年被改名为C++(++可能就是指继承自C语言)。


C++的里程碑
1983-第一个商业化的C++编译器
1998-C++标准化委员会标准化了C++[C++98]
2003-发布了一个补丁包,无新特性[C++03]
2005-一个技术报告“库技术报告”(简称TR1)公布。
2011-引进了许多重要的特性,并且增强了C++标准库


从上面可以看出,这次迭代是有史以来最更改最大的(是的,STL或许增加很多)。


我们需要了解这个新标准吗??

当然要。越快越好。抵抗变化时我们人类的天性。当我们掌握的语言或项目不再变化处于静态时,我们开发者离失业也就不远了。我们喜欢动态变化的项目,对于编程语言也一样。变化时不可避免的,专家委员会经过近十年的头脑风暴的成果显然十分有意义。
及时我们对这次在代码迭代中的更新不感兴趣,快速浏览一下这些特性也会帮助我们避免在编码之前使用旧的编译器。再说,只是简单把编译器换成支持C++11特性的,我们即可享受到C++11带来的好处,因为他的标准库增强了,性能更好了。所以如果你的项目用STL容器或算法,尽快更换编译器。

C++11特性

下面这个表总结了C++11的特性以及VS2010是否支持这些特

C++11的特性以及VS2010是否支持这些特
特性           目的          vs2010支持?
auto可用性增强支持
decltype可用性增强支持
Trailing return types可用性和性能增强支持
Right angle brackets可用性增强支持
static_assert可用性增强支持
nullptr可用性和性能增强支持
Strongly typed enums类型安全增强部分支持
Rvalue references性能增强支持
Move semantics性能增强支持
long long性能增强支持
Override control可用性增强支持
Lambda expressions可用性和性能增强支持
preventing narrowing性能增强不支持
Range based for-loops可用性和性能增强不支持

STL增强
array支持                           
Really smart pointers [unique_ptr, shared_ptr, weak_ptr]支持
bind and function支持
tuple支持
unoredered_map,unordered_set支持
forward_list支持
…… 

C++11特性


auto 关键字
这个特性使C++更加易用。委员会赋予了auto新的含义(提醒读者,旧的auto关键修饰的变量是说明这个变量作用域限于局部,除非有关键字static、extern、static修饰,现在auto隐式转换为自动存储)
新的标准中,auto可以根据初始化的值或初始化表达式来推论其修饰变量的类型。

auto i=5;//i将会定义为int类型

int n=3;
double pi=3.14;
auto j=pi*n;//j将会是double类型

现在来看一个变量类型难以写的例子

//假设有一个Map,存储 int和map(int,int)
map<int, map<int,int> > _Map;
//定义这个map的迭代器
map<int, map<int,int>>::const_iterator itr1=_Map.begin();
//如果使用auto将会变得简单
const auto itr2=_Map.begin();
现在来看一个变量类型不易知的例子

template<class U, class V>
void Somefunction(U u, V v)
{ 
    ??? result = u*v; // result的类型是什么呢 ??? 

    // 使用auto的话,让编译器来决定result的类型
    auto result = u*v;
}

我将用继续讲解下个特性一遍你们了解auto带来的易用性。当使用lambda表达式时,auto也十分易用(我们很快介绍lambdas)。

关于这个特性的给几个例子:

1、我们可以使用const、volatile、引用&、右值rvalue引用&&(&&-将会很快了解)修饰auto关键

auto k = 5;
auto* pK = new auto(k);
auto** ppK = new auto(&k);
const auto n = 6; 
2、auto修饰的变量必须要初始化

auto m; // m 应该初始化
3、auto不能与类型修饰符一起用

auto int p; // 不能这样用
4、函数和模板的参数不能用auto修饰

void MyFunction(auto parameter){} // 不能用auto修饰函数参数 

template<auto T> // 没意义-不允许
void Fun(T t){}
5、在堆上定义的变量,如果用auto修饰,必须初始化
int* p = new auto(0); //正确
int* pp = new auto(); // 应该初始化
  
auto x = new auto(); // Hmmm ... 没初始化
   
auto* y = new auto(9); // 正确,这里y是int*
auto z = new auto(9); //正确,这里z是int* (不是int)
6、auto是一个类型占位符,但是auto不能定义自己的类型。因此auto不能用来做类中转换或者与sizeof和typeid相关的操作
int value = http://www.mamicode.com/123;>7、用一个auto修饰的多个变量必须初始化为同样的类型

auto x1 = 5, x2 = 5.0, x3='r';  // 这里太多类型,不能这样用
8、除非修饰引用,auto把变量类型转换为CV修饰符(const和Volatile修饰符)

const int i = 99;
auto j = i;       // j 是int类型,而不是const int
j = 100           // 正确,j不是const

// 试一下引用
auto& k = i;      // k是const int&
k = 100;          // 错误,ks是const

// 对volatile修饰符,同理
9、除非定义引用,否则auto将把数组退化为指针

int a[9];
auto j = a;
cout<<typeid(j).name()<<endl; // 将会输出 int*

auto& k = a;
cout<<typeid(k).name()<<endl; // T将会输出 int [9


decltype类型修饰符
return_value decltype(表达式)
return_value是表达式的类型参数


这样可以用来决定表达式的类型,正如Bjarne所暗示的,如果我们只是需要一个变量的类型,使用初始化auto是个简单的选择。如果我们需要非变量的类型,例如
返回值类型,那么decltype就是我们需要的。
现在让我们看看刚才的一个例子
template<class U, class V>
void Somefunction(U u, V v)
{ 
   result = u*v; // result是什么类型呢

   decltype(u*v) result = u*v; // 哈哈 ....我们得到了我们想要的
}
在下一节,我将会介绍联合使用auto和decltype来声明模板函数,模板函数的返回值局定于模板的参数


使用decltype的几点建议:
1、如果表达式是一个函数,那么decltype的类型是函数的返回类型

1、如果表达式是一个函数,那么decltype的类型是函数的返回类型
int add(int i, int j) { return i+j; }

decltype( add(5,6) ) var = 5; // var的类型是add函数的返回值类型-int

2、如果表达式是左值,那么decltype

struct M { double x; };

double pi = 3.14;
const M* m = new M();
decltype( (m->x) ) piRef = pi;

    // 注意,由于内部括号,内部的声明被当做表达式
    // 而不是当做成员变量x, x类型是double,且是左值
    // declspec是double&,‘m’是const指针
    // 所以返回是const double&
    // 所以变量piRef的类型是const double&

3、有一点值得注意,decltype不像auto估算表达式,它只是推断表达式

int foo(){}
    decltype( foo() ) x; // x是int类型,在运行时不调用foo(


结尾修饰返回类型
这个是C++11的全新特性。直到现在,函数的返回值类型应在函数名前面。从C++11起,我们可以把函数返回类型放在函数声明的后面,仅仅在替换
auto修饰的返回类型。现在我们来看一下为什么这样做:

template<class U, class V>
??? Multiply(U u, V v)    // 怎么确定返回值类型
{ 
   return u*v;
}

我们不能这样做:
template<class U, class V>
decltype(u*v) Multiply(U u, V v)    // 因为在Multiply之前,u和v没有定义,怎么办!!
{ 
   return u*v;
}

在这种情况下,我们可以使用auto,一旦u和v定义知道时,我们就可以确定返回值类型。
很酷,不是吗?

template<class U, class V>
auto Multiply(U u, V v) -> decltype(u*v)    // 注意->在定义函数括号之后.
{ 
   return u*v;
}

右尖括号
看这个声明,
map<int, vector<int>> _Map;
在早些的编译器中这是错误的,因为在两个右尖括号之间没有空格,编译器将会把它当做右移操作符。
但是C++11编译器将会语法分析模板参数结束时的多重右尖括号,让我们免于在‘>‘输入空格。
与其他特性相比,这个不一个大的特性。但是C++开发者追求完美,这就是一个例子。