页面

搜索

2012年2月11日星期六

c++11标准带来的最显著变化(及为何该引起你的注意)

c++11标准带来的最显著变化(及为何该引起你的注意):

译者 鬼重

Bjarne Stroustrup, the creator of C++, said recently that C++11 “feels like a new language — the pieces just fit together better.” Indeed, core C++11 has changed
significantly. It now supports lambda expressions, automatic type
deduction of objects, uniform initialization syntax, delegating
constructors, deleted and defaulted function declarations,nullptr,
and most importantly, rvalue references — a feature that augurs a
paradigm shift in how one conceives and handles objects. And that’s just
a sample.

Bjarne
Stroustrup,c++首创人,最近表示:c++11“感觉象一种新语言——各部分彼此更协调了”。确实,核心c++11已发生显著的变化。它现在支持lambda表达式,对象的自动类型推定,统一的初始化语法,委托构造函数,函数声明显式指定可删除及显式指定缺省,空指针关键字,以及最重要的右值引用——一种特性:兆示着在如何预想和处置对象上的范式发生了转化。以上只是举例。

The C++11 Standard Library was also revamped with new algorithms, new
container classes, atomic operations, type traits, regular expressions,
new smart pointers,async()facility, and of course a multithreading library.

c++11标准库也获得修订,加入了新算法,新容器类,原子操作,type
traits,正则表达式,新型灵巧指针,异步方法,当然还有多线程库。

A complete list of the new core and library features of C++11 is available here.

新核心与库功能的完整清单点此跳转。

After the approval of the C++ standard in 1998, two committee members
prophesied that the next C++ standard would “certainly” include a
built-in garbage collector (GC), and that it probably wouldn’t support
multithreading because of the technical complexities involved in
defining a portable threading model. Thirteen years later, the new C++
standard, C++11, is almost complete. Guess what? It lacks a GC but it
does include a state-of–the-art threading library.

标准在1998年通过之后,两位会议委员曾预言下一代c++标准必将具备某种内置的废物回收器(缩写为GC),而且可能不会支持多线程,因为在可移植线程模型的定义上,所涉及的技术复杂度不确定。13年后,新的标准c++11接近完成。意外的是,GC缺席了,反倒具有了一种顶尖的线程库。

In this article I explain the biggest changes in the language, and
why they are such a big deal. As you’ll see, threading libraries are not
the only change. The new standard builds on the decades of expertise
and makes C++ even more relevant. As Rogers Cadenhead points out, “That’s pretty amazing for something as old as disco, Pet Rocks, and Olympic swimmers with chest hair.”

在本文当中,我试图阐述标准中最显著的变化,以及这其中究竟是怎么一回事。你会看到,线程库并非其唯一的变化。新标准基于专家数十载的努力,令c++更其妥贴可用。就如Rogers
Cadenhead指出的那样:“如同迪斯科、Pet
Rocks(译者注:某种过时的美国游戏),或是类如大龄奥运游泳选手一般,c++已经老气横秋,而新出标准相当地令人惊喜”。

First, let’s look at some of the prominent C++11 core-language features.


Lambda Expressions

首先,来看看有些什么突出的语言特性。

A lambda expression lets you define functions locally, at the place
of the call, thereby eliminating much of the tedium and security risks
that function objects incur. A lambda expression has the form:

Lambda表达式

用Lambda表达式可实现在调用地点定义局部函数,这么一来,显著避免了因启用函数对象而面临的繁琐表达和安全上的风险。一条lambda表达式有如下形式:


[capture](parameters)->return-type {body}



[capture](parameters)->return-type {body}

The[]construct inside a function call’s argument list indicates the beginning of a lambda expression. Let’s see a lambda example.

在一个函数调用的参数表内出现的[]方括弧构造,标示着一个lambda表达式的存在。我们来看一个例子。

Suppose you want to count how many uppercase letters a string contains. Usingfor_each()to traverses a char array, the following lambda expression determines
whether each letter is in uppercase. For every uppercase letter it
finds, the lambda expression incrementsUppercase, a variable defined outside the lambda expression:

假定要对字符串容器内的大写字母进行计数。利用标准库函数for_each遍历字符组,而如下列出的lambda式子来测定每个字母是否为大写。每个大写字母,一旦找出,则lambda式子把变量Uppercase加一,该变量定义在lambda表达式之外。



int main()
{
char s[]="Hello World!";
int Uppercase = 0; //modified by the lambda
for_each(s, s+sizeof(s), [&Uppercase] (char c) {
if (isupper(c))
Uppercase++;
});
cout

int main()

{
char s[]="Hello World!";
int Uppercase = 0; //modified by the lambda
for_each(s, s+sizeof(s), [&Uppercase] (char c) {
if (isupper(c))
Uppercase++;
});
cout<< Uppercase<<" uppercase letters in: "<< s<
}

It’s as if you defined a function whose body is placed inside another function call. The ampersand in [&Uppercase] means that the lambda body gets a reference to Uppercase so it can modify it. Without the ampersand, Uppercase would be passed by value. C++11 lambdas include constructs for member functions as well.



Automatic Type Deduction anddecltype

这个表达式看似如同置身于函数调用体内部的一种函数定义代码。用&符号引导的Uppercase表示lambda表达式需要获取一个对变量Uppercase的引用作为实参,藉此,便可以修改该变量。如果不要&符号,Uppercase则作值传递。c++11的lambda包含也着针对成员函数的构造设计。

In C++03, you must specify the type of an object when you declare it.
Yet in many cases, an object’s declaration includes an initializer.
C++11 takes advantage of this, letting you declare objects without
specifying their types:

自动类型推定和delcltype(译者注:类型宣告)

在标准c++03下,对象的声明里必须指定对象的类型。然而多种情形下,对象的声明含有一个初始化要素。标准c++11则利用这一点,在声明对象时,不必显式指定他们的类型:



auto x=0; //x has type int because 0 is int
auto c='a'; //char
auto d=0.5; //double
auto national_debt=14400000000000LL;//long long

auto x=0; //x has type int because 0 is int

auto c='a'; //char
auto d=0.5; //double
auto national_debt=14400000000000LL;//long long

Automatic type deduction is chiefly useful when the type of the
object is verbose or when it’s automatically generated (in templates).
Consider:

在对象类型太冗长或是(模板情形下)自动生成时,自动类型推定在简化表达上起主导作用。



void func(const vector&vi)
{
vector::const_iterator ci=vi.begin();
}

void func(const vector &vi)

{
vector::const_iterator ci=vi.begin();
}

Instead, you can declare the iterator like this:


象这样声明迭代子:

auto ci=vi.begin();

auto ci=vi.begin();

The keywordautoisn’t new; it actually dates back the pre-ANSI C era. However, C++11 has changed its meaning;autono longer designates an object with automatic storage type. Rather, it
declares an object whose type is deducible from its initializer. The old
meaning ofautowas removed from C++11 to avoid confusion.

关键字auto 不是新货色;它可上溯至前ANSI C时代。而今,C++11修改了它的含义;auto不再意味着设定对象为自动存贮类型了。而是用于声明一个对象,且该对象的类型由对象的初始化表达式推定出来。

C++11 offers a similar mechanism for capturing the type of an object or an expression. The new operatordecltypetakes an expression and “returns” its type:

C++11提供一种类似机制来捕获对象或表达式的类型。新进操作符decltype接受一个表达式,“返回”该表达式的类型:

const vectorvi;
typedef decltype (vi.begin()) CIT;
CIT another_const_iterator;

Uniform Initialization Syntax

const vector vi;

typedef decltype (vi.begin()) CIT;
CIT another_const_iterator;

C++ has at least four different initialization notations, some of which overlap.

统一的初始化语法

c++至少具有四种不同的初始化表示法,部分是重叠的。

Parenthesized initialization looks like this:


带括弧的初始化看起来象这样:

std::string s("hello");
int m=int(); //default initialization

std::string s("hello");

int m=int(); //default initialization

You can also use the=notation for the same purpose in certain cases:


也可以用 =
记号 来达成某些情形下的初始化目的:

std::string s="hello";
int x=5;

std::string s="hello";

int x=5;

For POD aggregates, you use braces:


对于POD(Plain Old Data的缩写,这种类型具有C兼容特点)性质的集合体,可用大括弧:

int arr[4]={0,1,2,3};
struct tm today={0};

int arr[4]={0,1,2,3};

struct tm today={0};

Finally, constructors use member initializers:

最后一种情况,构造函数则使用成员初始化(列表):

struct S {
int x;
S(): x(0) {} };

struct S {

 int x;
S(): x(0) {} };

This proliferation is a fertile source for confusion, not only among
novices. Worse yet, in C++03 you can’t initialize POD array members and
POD arrays allocated usingnew[]. C++11 cleans up this mess with a uniform brace notation:

存在初始化的多种变体是富含困扰的根源,不仅对生手来说是麻烦,高手也如此。更糟的是,c++03标准之下,给POD数组分配堆要使用new[]记号,且无法初始化这种情况下的POD数组成员。c++11清除了这种混乱,它启用了统一的大括弧记号:

class C
{
int a;
int b;
public:
C(int i, int j);
};

C c {0,0}; //C++11 only. Equivalent to: C c(0,0);

int* a = new int[3] { 1, 2, 0 }; /C++11 only

class X {
int a[4];
public:
X() : a{1,2,3,4} {} //C++11, member array initializer
};

class C

{
int a;
int b;
public:
C(int i, int j);
};

C c {0,0}; //C++11 only. Equivalent to: C c(0,0);

int* a = new int[3] { 1, 2, 0 }; /C++11 only

class X {
int a[4];
public:
X() : a{1,2,3,4} {} //C++11, member array initializer
};

With respect to containers, you can say goodbye to a long list ofpush_back()calls. In C++11 you can initialize containers intuitively:

针对容器来说,现在可以淘汰充斥着push_back方法的冗长代码了(译者注:有时会使用循环结构)。C++11允许直观地表示容器初始化:

// C++11 container initializer
vectorvs={ "first", "second", "third"};
map singers =
{ {"Lady Gaga", "+1 (212) 555-7890"},
{"Beyonce Knowles", "+1 (212) 555-0987"}};

// C++11 container initializer

vector vs={ "first", "second", "third"};
map singers =
{ {"Lady Gaga", "+1 (212) 555-7890"},
{"Beyonce Knowles", "+1 (212) 555-0987"}};

Similarly, C++11 supports in-class initialization of data members:

类似的,C++11
支持类的数据成员就地初始化:

class C
{
int a=7; //C++11 only
public:
C();
};

Deleted and Defaulted Functions

class C

{
int a=7; //C++11 only
public:
C();
};

A function in the form:

显式指定删除及显式指定缺省的函数声明

具备如下函数形式:

struct A
{
A()=default; //C++11
virtual ~A()=default; //C++11
};

struct A

{
A()=default; //C++11
virtual ~A()=default; //C++11
};

is called a defaulted function. The=default;part instructs the compiler to generate the default implementation for
the function. Defaulted functions have two advantages: They are more
efficient than manual implementations, and they rid the programmer from
the chore of defining those functions manually.

称之为缺省的函数。“=default;”记号部分指示编译器生成函数的缺省实作体。缺省的函数有两处优势:其一,比手工实作出来的代码更有效率,其二,把程序员从手工定义那些函数所引发的琐细事务中解脱出来。

The opposite of a defaulted function is a deleted function:

与缺省函数对应,也存在指定删除函数:

int func()=delete;

int func()=delete;

Deleted functions are useful for preventing object copying, among the
rest. Recall that C++ automatically declares a copy constructor and an
assignment operator for classes. To disable copying, declare these two
special member functions=delete:

声明删除的函数在防止复制对象的方面尤其有用。C++类自动声明一个复制构造函数和一个赋值操作。要禁止这种默认行为,使用记号=delete:

struct NoCopy
{
NoCopy & operator =( const NoCopy & ) = delete;
NoCopy ( const NoCopy & ) = delete;
};
NoCopy a;
NoCopy b(a); //compilation error, copy ctor is deleted

nullptr

struct NoCopy

{
NoCopy & operator =( const NoCopy & ) = delete;
NoCopy ( const NoCopy & ) = delete;
};
NoCopy a;
NoCopy b(a); //compilation error, copy ctor is deleted

At last, C++ has a keyword that designates a null pointer constant.nullptrreplaces the bug-proneNULLmacro and the literal 0 that have been used as null pointer substitutes for many years.nullptris strongly-typed:

nullptr

C++终于提供了一个关键字用于表示空指针常量。nullptr替代了有隐患的宏NULL和文字量0,

它们一直以来被当作空指针代用品,已行之有年。现在,nullptr具备了强类型的性质(译者注:c++的宗旨之一是作为一种强类型语言,参考《C++语言的设计与演化》):

void f(int); //#1
void f(char *);//#2
//C++03
f(0); //which f is called?
//C++11
f(nullptr) //unambiguous, calls #2

void f(int); //#1

void f(char *);//#2
//C++03
f(0); //which f is called?
//C++11
f(nullptr) //unambiguous, calls #2

nullptris applicable to all pointer categories, including function pointers and pointers to members:

nullptr适用于所有指针类型,包括函数指针和类成员指针:

const char *pc=str.c_str(); //data pointers
if (pc!=nullptr)
cout

const char *pc=str.c_str(); //data pointers

if (pc!=nullptr)
cout<
int (A::*pmf)()=nullptr; //pointer to member function
void (*pmf)()=nullptr; //pointer to function

Delegating Constructors

委托构造函数

In C++11 a constructor may call another constructor of the same class:


构造函数可能要调用本类中的其它构造函数:

class M //C++11 delegating constructors
{
int x, y;
char *p;
public:
M(int v) : x(v), y(0), p(new char [MAX]) {} //#1 target
M(): M(0) {cout

class M //C++11 delegating constructors

{
int x, y;
char *p;
public:
M(int v) : x(v), y(0), p(new char [MAX]) {} //#1 target
M(): M(0) {cout<<"delegating ctor"<
};

Constructor #2, the delegating constructor, invokes the target constructor #1.



Rvalue References

2号构造函数为委托者,由它调用目标:即1号构造函数。

Reference types in C++03 can only bind to lvalues. C++11 introduces a new category of reference types called rvalue references. Rvalue references can bind to rvalues, e.g. temporary objects and literals.

右值引用

在 C++03中,引用类型只能与左值绑定。
C++11引入了新的引用类型称为右值引用。右值引用能与右值相绑定,例如临时对象和文字量。

The primary reason for adding rvalue references is move semantics. Unlike traditional copying, moving means that a target object pilfers the resources of the source object, leaving the source in an “empty”
state. In certain cases where making a copy of an object is both
expensive and unnecessary, a move operation can be used instead. To
appreciate the performance gains of move semantics, consider string
swapping. A naive implementation would look like this:


增加右值引用的首要因素在于体现移动语义。与传统的复制不同,移动是指目的对象占据源对象的资源为己有,而源对象失去对资源的隶属关系。在某些情形中,复制对象的开销太大且没有必要,移动操作是其合理的替代。为感受移动语义在效率上好处,我们来看一个字串交换的过程。试看如下精简后的代码:

void naiveswap(string &a, string & b)
{
string temp = a;
a=b;
b=temp;
}

void naiveswap(string &a, string & b)

{
string temp = a;
a=b;
b=temp;
}

This is expensive. Copying a string entails the allocation of raw
memory and copying the characters from the source to the target. In
contrast, moving strings merely swaps two data members, without
allocating memory, copying char arrays and deleting memory:

这种开销是不合理的。复制整个串将引发堆分配,然后从源对象复制数据至目的地。相比而言,移动串只是交换数据成员(译者注:是地址),而不必引起内存分配,复制字符组和删除内存这一系列的动作开销:

void moveswapstr(string& empty, string & filled)
{
//pseudo code, but you get the idea
size_t sz=empty.size();
const char *p= empty.data();
//move filled's resources to empty
empty.setsize(filled.size());
empty.setdata(filled.data());
//filled becomes empty
filled.setsize(sz);
filled.setdata(p);
}

void moveswapstr(string& empty, string & filled)

{
//pseudo code, but you get the idea
size_t sz=empty.size();
const char *p= empty.data();
//move filled's resources to empty
empty.setsize(filled.size());
empty.setdata(filled.data());
//filled becomes empty
filled.setsize(sz);
filled.setdata(p);
}

If you’re implementing a class that supports moving, you can declare a
move constructor and a move assignment operator like this:

假定实现一个支持移动的类,可以这么声明移动构造函数和移动赋值操作符:

class Movable
{
Movable (Movable&&); //move constructor
Movable&& operator=(Movable&&); //move assignment operator
};

class Movable

{
Movable (Movable&&); //move constructor
Movable&& operator=(Movable&&); //move assignment operator
};

The C++11 Standard Library uses move semantics extensively. Many algorithms and containers are now move-optimized.


C++11 Standard Library

C++11标准库广泛使用移动语义。许多算法和容器目前都享有移动语义带来的优化。

C++ underwent a major facelift in 2003 in the form of the Library Technical Report 1 (TR1). TR1 included new container classes (unordered_set,unordered_map,unordered_multiset, andunordered_multimap)
and several new libraries for regular expressions, tuples, function
object wrapper and more. With the approval of C++11, TR1 is officially
incorporated into standard C++ standard, along with new libraries that
have been added since TR1. Here are some of the C++11 Standard Library
features:


Threading Library

2003
年,以库技术报告1(TR1)的形式,使得C++经历了一场重大改观。
TR1含有新容器类型族(unordered_set,unordered_map,unordered_multiset,
unordered_multimap),以及数个新进功能:正则表达式,
tuples(译者注:元组收纳器),函数对象封装器等等。伴随标准
C++11的生效,TR1则正式集成到C++标准之内,这意味着TR1一直以来所具备的全新库功能已经可用。这里列举一部分C++11的库功能:

Unquestionably, the most important addition to C++11 from a
programmer’s perspective is concurrency. C++11 has a thread class that
represents an execution thread, promises and futures, which are objects that are used for synchronization in a concurrent environment, the async() function template for launching concurrent tasks, and the thread_local storage type for declaring thread-unique data. For a quick tour of the C++11 threading library, read Anthony Williams’ Simpler Multithreading in C++0x.


New Smart Pointer Classes

线程库
毫无疑问,以编程员的角度看,最重大的改进在于并发主题。
C++11引入了线程类来表示执行线程,promises

futures(译者注:并发编程设计领域的术语,参考wiki)——指在并发环境中用于同步的对象,针对启动并发任务而设计的async()函数模板,及针对声明线程专有数据(thread-unique
data )的存储类型thread_local。欲对c++11线程库进行一次快餐体验,请阅读
Anthony
Williams撰写的《c++0x,更简明的多线程》(Simpler
Multithreading in c++0x)。

C++98 defined only one smart pointer class,auto_ptr, which is now deprecated. C++11 includes new smart pointer classes: shared_ptr and the recently-added unique_ptr.
Both are compatible with other Standard Library components, so you can
safely store these smart pointers in standard containers and manipulate
them with standard algorithms.


New Algorithms

新灵巧指针类

C++98只定义了一种灵巧指针类,auto_ptr,现在已过时。C++11
具备一些全新的灵巧指针类:shared_ptr,及最近才加入的unique_ptr。两者与其它标准库组件兼容,故此在标准容器内能够安全地保存这些灵巧指针,也能使用标准算法操作这些对象。

The C++11 Standard Library defines new algorithms that mimic the set theory operationsall_of(),any_of()andnone_of(). The following listing applies the predicateispositive()to the range[first, first+n)and usesall_of(),any_of()andnone_of()to examine the range's properties:

新算法:

C++11标准库定义了新的算法,即模拟集合论领域的操作符all_of(),any_of(),none_of()。下列代码清单里,对[first,first+n]所限定的范围内,使用谓词方法ispositive()遍历检查每个元素,并且采用all_of(),any_of(),none_of()方法,来测定该范围整体的性质。

#include//C++11 code
//are all of the elements positive?
all_of(first, first+n, ispositive()); //false
//is there at least one positive element?
any_of(first, first+n, ispositive());//true
// are none of the elements positive?
none_of(first, first+n, ispositive()); //false

#include

//C++11 code
//are all of the elements positive?
all_of(first, first+n, ispositive()); //false
//is there at least one positive element?
any_of(first, first+n, ispositive());//true
// are none of the elements positive?
none_of(first, first+n, ispositive()); //false

A new category ofcopy_nalgorithms is also available. Usingcopy_n(), copying an array of 5 elements to another array is a cinch:

一类新型copy_n算法也出笼了。利用copy_n()复制含有5个成员的数组到另一数组,可说是小菜一碟。

#include
int source[5]={0,12,34,50,80};
int target[5];
//copy 5 elements from source to target
copy_n(source,5,target);

#include

int source[5]={0,12,34,50,80};
int target[5];
//copy 5 elements from source to target
copy_n(source,5,target);

The algorithmiota()creates a range of sequentially increasing values, as if by assigning an initial value to*first, then incrementing that value using prefix ++. In the following listing,iota()assigns the consecutive values {10,11,12,13,14} to the arrayarr, and {'a', 'b', 'c'} to the char arrayc.

算法iota负责创建有限的递增值序列,通过向*first表示的第一子项指定初始值,那么该值则被施加前缀++操作,以生成后序数据。

includeint a[5]={0};
char c[3]={0};
iota(a, a+5, 10); //changes a to {10,11,12,13,14}
iota(c, c+3, 'a'); //{'a','b','c'}

include

int a[5]={0};
char c[3]={0};
iota(a, a+5, 10); //changes a to {10,11,12,13,14}
iota(c, c+3, 'a'); //{'a','b','c'}

C++11 still lacks a few useful libraries such as an XML API, sockets,
GUI, reflection — and yes, a proper automated garbage collector.
However, it does offer plenty of new features that will make C++ more
secure, efficient (yes, even more efficient than it has been thus far!
See Google's benchmark tests), and easier to learn and use.

C++11仍然缺少一些实用库,例如XML应用接口,sockets,图形用户界面,reflection——当然,还有合理的自动化废物回收器。新标准确实提供了大量新功能,使得C++更形可靠,高效(是滴,这是迄今为止最高效的!参见谷歌的benchmark
tests),易于学习和使用。

If the changes in C++11 seem overwhelming, don't be alarmed. Take the
time to digest these changes gradually. At the end of this process you
will probably agree with Stroustrup: C++11 does feel like a new language — a much better one.

是否这些C++11
下的变化似乎有点动静太那个猛啦,要淡定。费点时间,循序渐进地领会这些新变化。最终,你说不定会赞同
Stroustrup的表述:
C++11的确感觉象种新语言——更好的那种。