文章目录
  1. 1. C++11的新特性
    1. 1.1. 重要的细节语法调整
      1. 1.1.1. 模板表达式中的空格
      2. 1.1.2. nullptr and std::nullptr_t(空指针和空指针类型)
    2. 1.2. 使用auto自动推断类型
    3. 1.3. 统一的初始化和初始化列表
    4. 1.4. 基于区间的for循环

本文的大部分内容翻译自The C++ Standard Library A Tutorial and Reference (2nd Edition)的第三章,书中只列出了一些比较主要的新特性,更多比较详细的C++11特性可以参考C++11FAQThe C++ Programming Language (4th Edition)

C++11的新特性

重要的细节语法调整

首先,我想介绍2个C++11中虽然很细节但是对日常编程很重要的新特性。

模板表达式中的空格

原先在模板表达式的2个右括号中间需要加空格,现在不需要了

1
2
vector<list<int> >; // OK in each C++ version
vector<list<int>>; // OK since C++11

在整本书(包括实际代码)中,你会发现这2种形式。

nullptr and std::nullptr_t(空指针和空指针类型)

在C++11中,你可以使用nullptr表示一个指向空值的指针(注意,这与未定义值不同),而不用再使用0NULL了。这个特性对防止空指针被解释成整形数值的发生特别有用。例如:

1
2
3
4
5
void f(int);
void f(void*);
f(0); // calls f(int)
f(NULL); // calls f(int) if NULL is 0, ambiguous otherwise
f(nullptr); // calls f(void*)

nullptr是新关键字,它可以自动的转换成任意类型的指针但无法转换为整形数值,其类型是std::nullptr_t,在头文件<cstddef>中定义。所以,你现在可以重载那些需要传入空指针情况的操作,需要注意的是std::nullptr_t是一个基本数据类型(see Section 5.4.2, page 127)。

使用auto自动推断类型

在C++11中,可以使用auto1声明一个变量或对象而不用指明其具体类型。例如:

1
2
3
auto i = 42; // i has type int
double f();
auto d = f(); // d has type double

使用auto时,变量的类型是从初始化对象中推断得到的,所以,此时必须对变量做初始化:

1
auto i; // ERROR: can’t dedulce the type of i

也可以使用其他限制符2,例如:

1
static auto vat = 0.19;

对于表达式长而复杂的类型,auto非常有用。例如:

1
2
3
4
5
6
7
vector<string> v;
...
auto pos = v.begin(); // pos has type vector<string>::iterator
auto l = [] (int x) -> bool { // l has the type of a lambda
..., // taking an int and returning a bool
};

上述后半部分表示一个lambda对象,在Section 3.1.10, page 28中会有介绍。

统一的初始化和初始化列表

在C++11之前,程序员,尤其是初学者,很容易对如何初始化一个变量或对象感到困惑。因为初始化可以用括号、大括号以及赋值操作来完成。

基于这个原因,C++11引入了统一初始化的概念,即可以用一个统一的语法完成所有的初始化。这个语法就是使用大括号,如下:

1
2
3
4
5
6
int values[] { 1, 2, 3 };
std::vector<int> v { 2, 3, 5, 7, 11, 13, 17 };
std::vector<std::string> cities {
"Berlin", "New York", "London", "Braunschweig", "Cairo", "Cologne"
};
std::complex<double> c{4.0,3.0}; // equivalent to c(4.0,3.0)

初始化列表强制执行值初始化的操作,也就是说,即使是通常只有未定义值的基础类型的局部变量也会被初始化为0(如果是指针的话,会被初始化为nullptr)。

1
2
3
4
int i; // i has undefined value
int j{}; // j is initialized by 0
int* p; // p has undefined value
int* q{}; // q is initialized by nullptr

但是注意,窄化初始化,即会丢失精度或使用传入对象变化后的值的初始化,是无法使用大括号的。例如:

1
2
3
4
5
6
7
8
int x1(5.3); // OK, but OUCH: x1 becomes 5
int x2 = 5.3; // OK, but OUCH: x2 becomes 5
int x3{5.0}; // ERROR: narrowing
int x4 = {5.3}; // ERROR: narrowing
char c1{7}; // OK: even though 7 is an int, this is not narrowing
char c2{99999}; // ERROR: narrowing (if 99999 doesn’t fit into a char)
std::vector<int> v1 { 1, 2, 4, 5 }; // OK
std::vector<int> v2 { 1, 2.3, 4, 5.6 }; // ERROR: narrowing doubles to ints

如上所示,为了检查是否已经应用了窄化,只要编译期可以取值,即使是立即数也需要检查。关于这个例子,正如Bjarne Stroustrup在[Stroustrup:FAQ]中写道:"C++11避免很多不兼容性的方法是当它可以判定初始化对象何处发生了窄化(而非仅仅从类型判定)时,依赖初始化对象的实际值(如上例中的7)。如果传入值可以精确的表示目标值类型的话,这样的类型转换就不是窄化。注意,浮点数转换为整形总是发生了窄化,即使是7.0转换成7这种情况。"

为了能支持用户自定义的初始化列表,C++11提供了类模板std::initializer_list<>,它可以支持用一组值来执行初始化或者其他任何情况下你希望处理一组值。例如:

1
2
3
4
5
6
7
8
void print (std::initializer_list<int> vals)
{
for (auto p=vals.begin(); p!=vals.end(); ++p) { // process a list of values
std::cout << *p << "\n";
}
}
print ({12,3,5,7,11,13,17}); // pass a list of values to print()

当固定参数的构造函数和初始化列表的构造函数同时存在时,初始化列表会被优先考虑:

1
2
3
4
5
6
7
8
9
10
class P
{
public:
P(int,int);
P(std::initializer_list<int>);
};
P p(77,5); // calls P::P(int,int)
P q{77,5}; // calls P::P(initializer_list)
P r{77,5,42}; // calls P::P(initializer_list)
P s = {77,5}; // calls P::P(initializer_list)

如果没有初始化列表的构造函数的话,编译器会用2个int的构造函数初始化qs,而初始化r的动作是非法的。

由于初始化列表的引入,explicit 与拥有多个参数的构造函数变得更加相关。现在你可以禁止多个传入值情况下的自动类型转换,对使用"="语法的初始化也一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class P
{
public:
P(int a, int b) {
...
}
explicit P(int a, int b, int c) {
...
}
};
P x(77,5); // OK
P y{77,5}; // OK
P z {77,5,42}; // OK
P v = {77,5}; // OK (implicit type conversion allowed)
P w = {77,5,42}; // ERROR due to explicit (no implicit type conversion allowed)
void fp(const P&);
fp({47,11}); // OK, implicit conversion of {47,11} into P
fp({47,11,3}); // ERROR due to explicit
fp(P{47,11}); // OK, explicit conversion of {47,11} into P
fp(P{47,11,3}); // OK, explicit conversion of {47,11,3} into P

相同的情况,参数是初始化列表的explicit构造函数禁止含一个、多个或为空初始化列表的显式转换。

基于区间的for循环

C++11引入了新式的for循环,

1
```

cpp

1
2

cpp

1
2

cpp

1
2

cpp

1
2

cpp

1
2

cpp ```


  1. 注意,auto是C中的旧关键字,表示一个变量是局部变量,和static相反。实际上从来没有使用过auto,因为不把变量声明为static就默认声明为auto

  2. 译注:限制符(qualifiers),例如const, volatile等

文章目录
  1. 1. C++11的新特性
    1. 1.1. 重要的细节语法调整
      1. 1.1.1. 模板表达式中的空格
      2. 1.1.2. nullptr and std::nullptr_t(空指针和空指针类型)
    2. 1.2. 使用auto自动推断类型
    3. 1.3. 统一的初始化和初始化列表
    4. 1.4. 基于区间的for循环

欢迎来到Valleylord的博客!

本博的文章尽量原创。