logo资料库

c++11的中文文档.pdf

第1页 / 共36页
第2页 / 共36页
第3页 / 共36页
第4页 / 共36页
第5页 / 共36页
第6页 / 共36页
第7页 / 共36页
第8页 / 共36页
资料共36页,剩余部分请下载后查看
C++11 维基百科,自由的百科全书 (重定向自C++0x) C++11,先前被称作C++0x,是目前计划中的C++编程语言的新标准。它将取代现行的C++标准ISO/IEC 14882,公开于1998年并于2003年更新,通称C++98以及C++03。新的标准将会包含核心语言的新机 能,而且会扩展C++标准程序库,并入了大部分的C++ Technical Report 1程序库(数学的特殊函数 除外)。最新的消息被公开在ISO C++ 委员会网站(英文) (http://www.open-std.org/jtc1/sc22 /wg21/docs/papers/) 。国际标准草案(N3126 (http://www.open-std.org/jtc1/sc22/wg21/docs /papers/2010/n3126.pdf) )于2010年8月公开,2010年11月对该草案进行了修订(N3225 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3225.pdf) )。 ISO/IEC JTC1/SC22/WG21 C++ 标准委员会目前的计划是在2010年8月之前完成对最终委员会草案的 投票,以及于2011年3月召开的标准会议完成国际标准的最终草案。然而,WG21预期ISO将要花费六个 月到一年的时间才能正式发布新的 C++ 标准。因此最快在2011年年底,我们才能见到新的C++标准。 为了能够如期完成,委员会决定致力于直至2006年为止的提案,忽略新的提案[1]。 像C++这样的编程语言,通过一种演化的的过程来发展其定义。这个过程不可避免地将引发与现有代 码的兼容问题,在C++的发展过程中偶尔会发生。不过根据Bjarne Stroustrup(C++的创始人并且是 委员会的一员)表示,新的标准将几乎100%兼容于现有标准。 目录 1 候选变更 2 C++核心语言的扩充 3 核心语言的运行期表现强化 3.1 右值引用和 move 语义 3.2 泛化的常数表示式 3.3 对POD定义的修正 4 核心语言建构期表现的加强 4.1 外部模板 5 核心语言使用性的加强 5.1 初始化列表 5.2 统一的初始化 5.3 类型推导 5.4 以范围为基础的 for 循环 5.5 Lambda函数与表示式 5.6 另一种的函数语法 5.7 对象建构的改良 5.8 显式虚函数重载 5.9 空指针 5.10 强类型枚举 5.11 角括号 5.12 显式类型转换子 5.13 模板的别名 5.14 无限制的unions 6 核心语言能力的提升 6.1 变长参数模板 6.2 新的字符串字面值 第1页 共36页 2011/11/15 下午 13:51
6.3 用户自定义的字面值 6.4 多任务存储器模型 6.5 thread-local的存储期限 6.6 使用或禁用对象的默认函数 6.7 long long int类型 6.8 静态assertion 6.9 允许sizeof操作符作用在类型的数据成员上,无须明确的对象 6.10 垃圾回收机制 7 C++标准程序库的变更 7.1 标准库组件上的升级 7.2 绪程支持 7.3 多元组类型 7.4 散列表 7.5 正则表达式 7.6 通用智能指针 7.7 可扩展的随机数功能 7.8 包装引用 7.9 多态函数对象包装器 7.10 用于元编程的类型属性 7.11 用于计算函数对象返回类型的统一方法 8 已被移除或是不包含在 C++11 标准的特色 9 将被移除或废弃的特色 10 关系项目 11 参考资料 11.1 C++标准委员会文件 11.2 文章 12 外部链接 候选变更 C++的修订包含核心语言以及标准程序库。 在发展新标准的每个机能上,委员会采取了几个方向: 维持与C++98,可能的话还有C之间的稳定性与兼容性; 尽可能不通过核心语言的扩展,而是通过标准程序库来引进新的特色; 能够演进编程技术的变更优先; 改进 C++ 以帮助系统以及库设计,而不是引进只针对特别应用的新特色; 增进类型安全,提供对现行不安全的技术更安全的替代方案; 增进直接对硬件工作的能力与表现; 提供现实世界中问题的适当解决方案; 实行“zero-overhead”原则(某些功能要求的额外支持只有在该功能被使用时才能使用); 使C++易于教授与学习 对初学者的注重被认为是重要的,因为他们构成了计算机程序员的主体。也因为许多初学者不愿扩展 他们对 C++ 的知识,只限于使用他们对 C++ 专精的部分。此外,考虑到 C++ 被广泛的使用(包含 应用领域和编程风格),即便是最有经验的程序员在面对新的编程范式时也会成为初学者。 C++核心语言的扩充 C++委员会的主要焦点是在语言核心的发展上。核心语言将被大幅改善的领域包括多线程支持、泛型 编程、统一的初始化,以及性能表现的加强。 第2页 共36页 2011/11/15 下午 13:51
在此分成4个区块来讨论核心语言的特色以及变更: 运行期表现强化、建构期表现强化、可用性强 化,还有新的功能。某些特色可能会同时属于多个区块,但在此仅于其最具代表性的区块描述该特 色。 核心语言的运行期表现强化 以下的语言机能主要用来提升某些性能表现,像是存储器或是速度上的表现。 右值引用和 move 语义 在 C++03及之前的标准,临时对象(称为右值"R-values",位于赋值操作符之右)无法被改变,在 C 中亦同(且被视为无法和 const T& 做出区分)。尽管在某些情况下临时对象的确会被改变,甚至也 被视为是一个有用的漏洞。 C++11 增加一个新的非常数引用(reference)类型,称作右值引用(R-value reference),标记为 T &&。右值引用所引用的临时对象可以在该临时对象被初始化之后做修改,这是为了允许 move 语 义。 C++03 性能上被长期被诟病的其中之一,就是其耗时且不必要的深度拷贝。深度拷贝会发生在当对象 是以传值的方式传递。举例而言,std::vector 是内部保存了 C-style 数组的一个包装,如果一 个std::vector的临时对象被建构或是从函数返回,要将其存储只能通过生成新的std::vector 并且把该临时对象所有的数据复制进去。该临时对象和其拥有的内存会被摧毁。(为了讨论上的方 便,这里忽略返回值优化) 在 C++11,一个std::vector的 "move 建构子" 对某个vector的右值引用可以单纯地从右值复制其内 部 C-style 数组的指针到新的 vector,然后留下空的右值。这个操作不需要数组的复制,而且空的 暂时对象的解构也不会摧毁存储器。传回vector暂时对象的函数只需要传回std::vector&&。如果 vector没有 move 建构子,那么复制建构子将被调用,以const std::vector &的正常形式。 如 果它确实有 move 建构子,那么就会调用 move 建构子,这能够免除大幅的存储器配置。 基于安全的理由,具名的变量将永远不被认定为右值,即使它是被如此声明的;为了获得右值必须使 用 std::move()。 bool is_r_value(int &&) { return true; } bool is_r_value(const int &) { return false; } void test(int && i) { is_r_value(i); // i 為具名變數,即使被宣告成右值也不會被認定是右值。 is_r_value(std::move(i)); // 使用 std::move() 取得右值。 } 由于右值引用的用语特性以及对于左值引用(L-value references;regular references)的某些用 语修正,右值引用允许开发者提供完美转发 (perfect function forwarding)。当与变长参数模板结 合,这项能力允许函数模板能够完美地转送引数给其他接受这些特定引数的函数。最大的用处在于转 送建构子参数,创造出能够自动为这些特定引数调用正确建构式的工厂函数(factory function)。 泛化的常数表示式 C++ 本来就已具备常数表示式(constant expression)的概念。像是 3+4 总是会产生相同的结果并 且没有任何的副作用。常数表示式对编译器来说是优化的机会,编译器时常在编译期运行它们并且将 值存入程序中。同样地,在许多场合下,C++ 规格要求使用常数表示式。例如在数组大小的定义上, 以及枚举值(enumerator values)都要求必须是常数表示式。 第3页 共36页 2011/11/15 下午 13:51
然而,常数表示式总是在遇上了函数调用或是对象建构式时就终结。所以像是以下的例子是不合法 的: int GetFive() {return 5;} int some_value[GetFive() + 5];// 欲產生 10 個整數的陣列。 不合法的 C++ 寫法 这不是合法的 C++,因为 GetFive() + 5 并不是常数表示式。编译器无从得知 GetFive 实际上在运 行期是常数。理论上而言,这个函数可能会影响全局变量,或者调用其他的非运行期(non- runtime)常数函数等。 C++11引进关键字 constexpr 允许用户保证函数或是对象建构式是编译期常数。以上的例子可以被写 成像是下面这样: constexpr int GetFive() {return 5;} int some_value[GetFive() + 5];// 欲產生 10 個整數的陣列。合法的C++11寫法 这使得编译器能够了解并去验证 GetFive 是个编译期常数。 对函数使用 constexpr 在函数可以做的事上面加上了非常严格的条件。首先,该函数的回返值类型 不能为 void。第二点,函数的内容必须依照 "return expr" 的形式。第三点,在引数取代后,expr 必须是个常数表示式。这些常数表示式只能够调用其他被定义为 constexpr 的函数,或是其他常数 表示式的数据变量。 最后一点,有着这样标签的函数直到在该编译单元内被定义之前是不能够被调 用的。 变量也可以被定义为常数表示式值: constexpr double forceOfGravity = 9.8; constexpr double moonGravity = forceOfGravity / 6.0; 常数表示式的数据变量是隐式的常数。他们可以只存储常数表示式或常数表示式建构式的结果。 为了从用户自定类型(user-defined type)建构常数表示式的数据变量,建构式也可以被声明成 constexpr。与常数表示式函数一样,常数表示式的建构式必须在该编译单元内使用之前被定义。他 必须有着空的函数本体。它必须用常数表示式初始化他的成员(member)。而这种类型的解构式应当 是无意义的(trivial),什么事都不做。 复制 constexpr 建构起来的类型也应该被定义为 constexpr,这样可以让他们从常数表示式的函数 以值传回。类型的任何成员函数,像是复制建构式、重载的操作符等等,只要他们符合常数表示式函 数的定义,都可以被声明成 constexpr。这使得编译器能够在编译期进行类型的复制、对他们施行运 算等等。 常数表示式函数或建构式,可以以非常数表示式(non-constexpr)参数唤起。就如同 constexpr 整 数字面值能够指派给 non-constexpr 变量,constexpr 函数也可以接受 non-constexpr 参数,其结 果存储于 non-constexpr 变量。constexpr 关键字只有当表示式的成员都是 constexpr,才允许编 译期常数性的可能。 对POD定义的修正 在标准C++,一个结构(struct)为了能够被当成plain old data (POD),必须遵守几条规则。有很 好的理由使我们想让大量的类型符合这种定义,符合这种定义的类型能够允许产生与C兼容的对象布 第4页 共36页 2011/11/15 下午 13:51
局(object layout)。然而,C++03的规则太严苛了。 C++11将会放宽关于POD的定义。 当class/struct是极简的(trivial)、属于标准布局(standard-layout),以及他的所有非静态(non- static)成员都是POD时,会被视为POD。 一个极简的类型或结构符合以下定义: 1. 2. 3. 4. 极简的默认建构式。这可以使用默认建构式语法,例如SomeConstructor() = default; 极简的复制建构式,可使用默认语法(default syntax) 极简的赋值操作符,可使用默认语法(default syntax) 极简的解构式,不可以是虚拟的(virtual) 一个标准布局(standard-layout)的类型或结构符合以下定义: 1. 2. 3. 4. 5. 6. 7. 只有非静态的(non-static)数据成员,且这些成员也是符合标准布局的类型 对所有non-static成员有相同的访问控制(public, private, protected) 没有虚函数 没有虚拟基类 只有符合标准布局的基类 没有和第一个定义的non-static成员相同类型的基类 若非没有带有non-static成员的基类,就是最底层(继承最末位)的类型没有non-static数据 成员而且至多一个带有non-static成员的基类。基本上,在该类型的继承体系中只会有一个类 型带有non-static成员。 核心语言建构期表现的加强 外部模板 在标准C++中,只要在编译单元内遇到被完整定义的模板,编译器都必须将其实例化 (instantiate)。这会大大增加编译时间,特别是模板在许多编译单元内使用相同的参数实例化。 看起来没有办法告诉C++不要引发模板的实例化。 C++11将会引入外部模板这一概念。C++已经有了强制编译器在特定位置开始实例化的语法: template class std::vector; 而C++所缺乏的是阻止编译器在某个编译单元内实例化模板的能力。C++11将简单地扩充前文语法如 下: extern template class std::vector; 这样就告诉编译器不要在该编译单元内将该模板实例化。 核心语言使用性的加强 这些特色存在的主要目的是为了使C++能够更容易使用。 举凡可以增进类型安全,减少代码重复,不 易误用代码之类的。 初始化列表 第5页 共36页 2011/11/15 下午 13:51
标准C++从C带来了初始化列表(initializer list)的概念。这个构想是结构或是数组能够依据成员 在该结构内定义的顺序通过给予的一串引数来产生。这些初始化列表是递归的,所以结构的数组或是 包含其他结构的结构可以使用它们。这对静态列表或是仅是把结构初始化为某值而言相当有用。C++ 有构造函数,能够重复对象的初始化。但单单只有那样并不足以取代这项特色的所有机能。除了这些 对象必须遵守POD的定义的限制条件,标准C++允许在结构或类型上使用这项机能;非POD的类型不能 使用,就连相当有用的C++-style容器像是std::vector也不行。 C++11将会把初始化列表的概念绑到类型上,称作std::initializer_list。这允许构造函数或其他函 数像参数般地使用初始化列表。举例来说: class SequenceClass { public: SequenceClass(std::initializer_list list); }; 这将允许SequenceClass由一连串的整数构造,就像: SequenceClass someVar = {1, 4, 5, 6}; 这个构造函数是种特殊的构造函数,称作初始化列表构造函数。有着这种构造函数的类型在统一初始 化的时候会被特别对待。 类型std::initializer_list<>是个第一级的C++11标准程序库类型。然而他们只能够经由C++11编译 器通过{}语法的使用被静态地构造 。这个列表一经构造便可复制,虽然这只是copy-by-reference。 初始化列表是常数;一旦被创建,其成员均不能被改变,成员中的数据也不能够被变动。 因为初始化列表是真实类型,除了类型构造式之外还能够被用在其他地方。正规的函数能够使用初始 化列表作为引数。例如: void FunctionName(std::initializer_list list); FunctionName({1.0f, -3.45f, -0.4f}); 标准容器也能够以这种方式初始化: vector v = { "xyzzy", "plugh", "abracadabra" }; 统一的初始化 标准 C++ 在初始化类型方面有着许多问题。初始化类型有数种方法,而且交换使用时不会都产生相 同结果。传统的建构式语法,看起来像是函数声明,而且为了能使编译器不会弄错必须采取一些步 骤。只有集合体和 POD 类型能够被集合式的初始化(使用 SomeType var = {/*stuff*/};). C++11 将会提供一种统一的语法初始化任意的对象,它扩充了初始化串行语法: struct BasicStruct { int x; 第6页 共36页 2011/11/15 下午 13:51
float y; }; struct AltStruct { AltStruct(int _x, float _y) : x(_x), y(_y) {} private: int x; float y; }; BasicStruct var1{5, 3.2f}; AltStruct var2{2, 4.3f}; var1 的初始化的运作就如同 C-style 的初始化串行。每个公开的变量将被对应于初始化串行的值给 初始化。隐式类型转换会在需要的时候被使用,这里的隐式类型转换不会产生范围缩限 (narrowing)。要是不能够转换,编译便会失败。(范围缩限 (narrowing):转换后的类型无法表示原 类型。如将 32-bit 的整数转换为 16-bit 或 8-bit 整数,或是浮点数转换为整数。) var2 的初始 化则是简单地调用建构式。 统一的初始化建构能够免除具体指定特定类型的必要: struct IdString { std::string name; int identifier; }; IdString var3{"SomeName", 4}; 该语法将会使用 const char * 参数初始化 std::string 。你也可以做像下面的事: IdString GetString() { return {"SomeName", 4}; // 注意這裡不需要明確的型別 } 统一初始化不会取代建构式语法。仍然会有需要用到建构式语法的时候。如果一个类型拥有初始化串 行建构式(TypeName(initializer_list);),而初始化串行符合 sequence 建构式的类 型,那么它比其他形式的建构式的优先权都来的高。C++11 版本的 std::vector 将会有初始化串行 建构式。这表示: std::vector theVec{4}; 这将会调用初始化串行建构式,而不是调用std::vector只接受一个尺寸参数产生相应尺寸 vector 的建构式。要使用这个建构式,用户必须直接使用标准的建构式语法。 类型推导 第7页 共36页 2011/11/15 下午 13:51
在标准 C++(和 C ),使用变量必须明确的指出其类型。然而,随着模版类型的出现以及模板超编 程的技巧,某物的类型,特别是函数定义明确的回返类型,就不容易表示。在这样的情况下,将中间 结果存储于变量是件困难的事,可能会需要知道特定的超编程程序库的内部情况。 C++11 提供两种方法缓解上述所遇到的困难。首先,有被明确初始化的变量可以使用 auto 关键字。 这会依据该初始化子(initializer)的具体类型产生变量: auto someStrangeCallableType = boost::bind(&SomeFunction, _2, _1, someObject); auto otherVariable = 5; someStrangeCallableType 的类型就是模板函数 boost::bind 对特定引数所回返的类型。作为编译 器语义分析责任的一部份,这个类型能够简单地被编译器决定,但用户要通过查看来判断类型就不是 那么容易的一件事了。 otherVariable 的类型同样也是定义明确的,但用户很容易就能判别。它是个 int(整数),就和整 数字面值的类型一样。 除此之外,decltype 能够被用来在编译期决定一个表示式的类型。举例: int someInt; decltype(someInt) otherIntegerVariable = 5; decltype 和 auto 一起使用会更为有用,因为 auto 变量的类型只有编译器知道。然而 decltype 对于那些大量运用操作符重载和特化的类型的代码的表示也非常有用。 auto 对于减少冗赘的代码也很有用。举例而言,程序员不用写像下面这样: for (vector::const_iterator itr = myvec.cbegin(); itr != myvec.cend(); ++itr) 而可以用更简短的 for (auto itr = myvec.cbegin(); itr != myvec.cend(); ++itr) 这项差异随着程序员开始嵌套容器而更为显著,虽然在这种情况下 typedef 是一个减少代码的好方 法。 decltype 所表示的类型可以和 auto 推导出来的不同。 #include int main() { const std::vector v(1); auto a = v[0];// a 為 int 型別 decltype(v[0])b; // b 為 const int& 型別,即 // std::vector::operator[](size_type)const 的回返型別 auto c = 0; // c 為 int 型別 auto d = c; // d 為 int 型別 decltype(c) e; // e 為 int 型別,c 實體的型別 decltype((c)) f = e; // f 為 int& 型別,因為(c)是左值 第8页 共36页 2011/11/15 下午 13:51
分享到:
收藏