非成员的begin和end函数。用法如下:
?
?
1 sort(begin(v), end(v));
2 sort(begin(a), end(a));
?
?
显式虚函数重载
?
在引入C++ 11之前,基类和派生类中的虚函数很容易产生错误使用的情况。比如:
?
a、基类添加了一个虚函数,但该虚函数与派生类中的某个已有普通函数相同。
?
b、派生类添加了一个普通函数,但该函数与基类中的某个已有虚函数相同。
?
为了避免这些情况,在C++ 11中可以使用override来显式地表明需要进行虚函数重载。比如:
?
?
1 class Base
2 {
3 virtual void some_func(float);
4 };
5
6 class Derived : public Base
7 {
8 virtual void some_func(int) override; // 将产生编译错误
9 virtual void some_func(float) override; // 正确
10 };
?
?
?
C++ 11中还引入了final指示符,用于防止类或接口被继承。比如:
?
?
?
1 class Base1 final { };
2 class Derived1 : public Base1 { }; // 将产生编译错误
3 class Base2
4 {
5 virtual void f() final;
6 };
7 class Derived2 : public Base2
8 {
9 void f(); // 将产生编译错误
10 };
?
?
C++ 03中,可以使用typedef给模板类指定一个新的类型名称,但却不能给类模板指定别名。比如:
?
1 template< typename first, typename second, int third>
2 class SomeType; template< typename second>
3 typedef SomeType
TypedefName; // 在C++ 03中是不合法的
?
?
?
无限制的union
?
在C++ 03中,并非任意的数据类型都能做为union的成员。比方说,带有non-trivial构造函数的类型就不能是 union 的成员。在C++ 11中,移除了所有对union的使用限制,除了其成员仍然不能是引用类型这种情况。
?
?
?
?
1 struct point
2 {
3 point() {}
4 point(int x, int y): m_x(x), m_y(y) {}
5 int m_x, m_y;
6 };
7 union
8 {
9 int z;
10 double w;
11 point p; // 在C++ 03中不合法;在C++ 11中合法
12 };
?
在C++ 11中,允许sizeof运算符作用在类型的数据成员上,而无须明确的对象。在C++ 03中,这是不允许的,会导致编译错误。比如:
1 struct SomeType { OtherType member; };
2 sizeof(SomeType::member); // 在C++ 03中不合法;在C++ 11中合法
?
新的算法
?
C++ 11中新增了一些比较实用的算法。比如all_of、any_of、none_of、copy_n、copy_if和iota等。参考代码如下:
?
?
1 int a[5] = {-2, -1, 0, 1, 2};
2 auto funIsPositive = [](int v){return v>0;};
3 bool bRet = all_of(a, a+5, funIsPositive); // false
4 bRet = any_of(a, a+5, funIsPositive); // true
5 bRet = none_of(a, a+5, funIsPositive); // false
6 int b[5] = {0};
7 copy_n(a, 5, b); // 将a开始的5个元素拷贝到b中
8 copy_if(a, a+5, b, funIsPositive); // 将1, 2两个数拷贝到b中
9 iota(a, a+5, 10); // a中的每个元素加10
?
?
泛化的常数表达式
?
C++ 03中本来就已经具有常数表示式的概念,比如:3+5,6*7等。常数表示式对编译器来说是优化的机会,编译器常在编译期运行它们并且将值存入程序中。同样地,在许多场合下,C++规范要求使用常数表示式。比如数组大小、枚举值等。
?
然而,常数表示式总是在遇到了函数调用时就终结。比如:
1 int GetFive() { return 5; }
2 int some_value[GetFive() + 5]; // 不合法
?
?
C++ 11引进关键字constexpr允许用户保证函数是编译期常数。比如:
1 constexpr int GetFive() { return 5; }
2 int some_value[GetFive() + 5];
?
?
?
?
?
C++ 11中引入的一个非常重要也是比较难于理解的新特性就是完美转发(Perfect Forwarding)。完美转发中有两个关键词:“转发”和“完美”。
我们先来看第一个关键词“转发”,那么在C++中,“转发”表示什么含义呢?转发通常用在模板编程中,假设有一个函数F(a1, a2, ..., an),如果存在另一个函数G(a1, a2, ..., an),调用G相当于调用了F,则我们说函数G将a1, a2, ..., an等参数正确地转发给了函数F。再来看第二个关键词“完美”,“完美”转发是相对于其他转发方案而言的。在目前已提出的7种转发方案中,只有完美转发能够正确地实现转发的语义,其他方案都存在这样或那样的问题。下面一一进行介绍。
转发方案一:使用非常量左值引用。考虑下面的代码。
?
?
?
?
1 void F(int a)
2 {
3 cout << a << endl;
4 }
5
6 template
7 void G(A &a)
8 {
9 F(a);
10 }
?
?
?
使用非常量左值引用时,我们可以调用F(10),但无法调用G(10),即我们无法接收非常量右值的参数。
?
?
?
转发方案二:使用常量左值引用。考虑下面的代码。
?
?
?
?
1 void F(int &a)
2 {
3 cout << a << endl;
4 }
5
6 template
7 void G(const A &a)
8 {
9 F(a);
10 }
?
?
?
使用常量左值引用时,函数G可以接收任意类型的值作为参数,包括非常量左值、常量左值、非常量右值和常量右值。但当F的参数类型为非常量左值引用时,我们无法将一个常量左值引用转发给一个非常量左值引用。
?
?
?
转发方案三:使用非常量左值引用 + 常量左值引用。考虑下面的代码。
?
?
?
?
1 template
2 void G(A &a)
3 {
4 F(a);
5 }
6
7 template
8 void G(const A &a)
9 {
10 F(a);
11 }
?
?
?
综合前面两种方案的分析结果,可以得出这种方案相当于对函数G进行了重载,此时可以接收任意类型的值作为参数,也可以顺利地实现转发。但由于使用了常量和非常量两种形式的重载,当参数的个数N较大时,需要重载的函数会呈指数级增长(2的N次方),因此这种方案实际上是不可取的。
?
?
?
转发方案四:使用常量左值引用 + const_cast。
?
?
1 template
2 void G(const A &a)
3 {
4 F(const_cast(a));
5 }
?
这种方案克服了方案二的缺点,现在可以将常量左值引用转发给非常量左值引用了。但