C++11

来源:互联网 发布:录像软件手机版 编辑:程序博客网 时间:2024/06/07 06:52


深度学习代码专栏     攒课--我的学习我做主     【hot】直播技术精选    
 

C++11

 11849人阅读 评论(0) 收藏 举报
 分类:

目录(?)[+]

C++11,先前被称作C++0x,即ISO/IEC 14882:2011,是目前的C++编程语言的正式标准。它取代第二版标准ISO/IEC 14882:2003(第一版ISO/IEC 14882:1998公开于1998年,第二版于2003年更新,分别通称C++98以及C++03,两者差异很小)。新的标准包含核心语言的新机能,而且扩展C++标准程序库,并入了大部分的C++ Technical Report 1程序库(数学的特殊函数除外)。最新的消息被公开在 ISO C++ 委员会网站(英文)。

ISO/IEC JTC1/SC22/WG21 C++ 标准委员会计划在2010年8月之前完成对最终委员会草案的投票,以及于2011年3月召开的标准会议完成国际标准的最终草案。然而,WG21预期ISO将要花费六个月到一年的时间才能正式发布新的 C++ 标准。为了能够如期完成,委员会决定致力于直至2006年为止的提案,忽略新的提案[1]。最终于2011年8月12日公布,并于2011年9月出版。

2012年2月28日的国际标准草案(N3376)是最接近于现行标准的草案,差异仅有编辑上的修正。

像C++这样的编程语言,通过一种演化的的过程来发展其定义。这个过程不可避免地将引发与现有代码的兼容问题,在C++的发展过程中偶尔会发生。不过根据Bjarne Stroustrup(C++的创始人并且是委员会的一员)表示,新的标准将几乎100%兼容于现有标准。

目录

  • 1候选变更
  • 2C++核心语言的扩充
  • 3核心语言的运行期表现强化
    • 3.1右值引用和 move 语义
    • 3.2泛化的常数表示式
    • 3.3对POD定义的修正
  • 4核心语言建构期表现的加强
    • 4.1外部模板
  • 5核心语言使用性的加强
    • 5.1初始化列表
    • 5.2统一的初始化
    • 5.3类型推导
    • 5.4以范围为基础的 for 循环
    • 5.5Lambda函数与表示式
    • 5.6另一种的函数语法
    • 5.7对象建构的改良
    • 5.8显式虚函数重载
    • 5.9空指针
    • 5.10强类型枚举
    • 5.11角括号
    • 5.12显式类型转换子
    • 5.13模板的别名
    • 5.14无限制的unions
  • 6核心语言能力的提升
    • 6.1变长参数模板
    • 6.2新的字符串字面值
    • 6.3用户自定义的字面值
    • 6.4多任务存储器模型
    • 6.5thread-local的存储期限
    • 6.6使用或禁用对象的默认函数
    • 6.7long long int类型
    • 6.8静态assertion
    • 6.9允许sizeof运算符作用在类型的数据成员上,无须明确的对象
    • 6.10垃圾回收机制
  • 7C++标准程序库的变更
    • 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关系项目
  • 12参考资料
    • 12.1C++标准委员会文件
    • 12.2文章
  • 13外部链接

候选变更

C++的修订包含核心语言以及标准程序库。

在发展新标准的每个机能上,委员会采取了几个方向:

  • 维持与C++98,可能的话还有C之间的稳定性与兼容性;
  • 尽可能不通过核心语言的扩展,而是通过标准程序库来引进新的特色;
  • 能够演进编程技术的变更优先;
  • 改进 C++ 以帮助系统以及库设计,而不是引进只针对特别应用的新特色;
  • 增进类型安全,提供对现行不安全的技术更安全的替代方案;
  • 增进直接对硬件工作的能力与表现;
  • 提供现实世界中问题的适当解决方案;
  • 实行“zero-overhead”原则(某些功能要求的额外支持只有在该功能被使用时才能使用);
  • 使C++易于教授与学习

对初学者的注重被认为是重要的,因为他们构成了计算机程序员的主体。也因为许多初学者不愿扩展他们对 C++ 的知识,只限于使用他们对 C++ 专精的部分。此外,考虑到 C++ 被广泛的使用(包含应用领域和编程风格),即便是最有经验的程序员在面对新的编程范式时也会成为初学者。

C++核心语言的扩充

C++委员会的主要焦点是在语言核心的发展上。核心语言将被大幅改善的领域包括多线程(或称为“多线程”)支持、泛型编程、统一的初始化,以及性能表现的加强。

在此分成4个区块来讨论核心语言的特色以及变更: 运行期表现强化、建构期表现强化、可用性强化,还有新的功能。某些特色可能会同时属于多个区块,但在此仅于其最具代表性的区块描述该特色。

核心语言的运行期表现强化

以下的语言机能主要用来提升某些性能表现,像是存储器或是速度上的表现。

右值引用和 move 语义

在 C++03及之前的标准,临时对象(称为右值"R-values",位于赋值运算符之右)无法被改变,在 C 中亦同(且被视为无法和 const T& 做出区分)。尽管在某些情况下临时对象的确会被改变,甚至也被视为是一个有用的漏洞。

C++11 增加一个新的非常数引用(reference)类型,称作右值引用(R-value reference),标记为T &&。右值引用所引用的临时对象可以在该临时对象被初始化之后做修改,这是为了允许 move 语义。

C++03 性能上被长期被诟病的其中之一,就是其耗时且不必要的深度拷贝。深度拷贝会发生在当对象是以传值的方式传递。举例而言,std::vector<T> 是内部保存了 C-style 数组的一个包装,如果一个std::vector<T>的临时对象被建构或是从函数返回,要将其存储只能通过生成新的std::vector<T>并且把该临时对象所有的数据复制进去。该临时对象和其拥有的内存会被摧毁。(为了讨论上的方便,这里忽略返回值优化)

在 C++11,一个std::vector的 "move 构造函数" 对某个vector的右值引用可以单纯地从右值复制其内部 C-style 数组的指针到新的 vector,然后留下空的右值。这个操作不需要数组的复制,而且空的暂时对象的解构也不会摧毁存储器。传回vector暂时对象的函数只需要传回std::vector<T>&&。如果vector没有 move 构造函数,那么复制构造函数将被调用,以const std::vector<T> &的正常形式。 如果它确实有 move 构造函数,那么就会调用 move 构造函数,这能够免除大幅的存储器配置。

基于安全的理由,具名的变量将永远不被认定为右值,即使它是被如此声明的;为了获得右值必须使用 std::move<T>()

<span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">bool</span> is_r_value<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span><span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">int</span> <span class="sy3" style="margin: 0px; padding: 0px; line-height: 1.5;">&&</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span> <span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span> <span class="kw1" style="margin: 0px; padding: 0px; line-height: 1.5;">return</span> <span class="kw2" style="margin: 0px; padding: 0px; line-height: 1.5;">true</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span> <span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span><span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">bool</span> is_r_value<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span><span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">const</span> <span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">int</span> <span class="sy3" style="margin: 0px; padding: 0px; line-height: 1.5;">&</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span> <span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span> <span class="kw1" style="margin: 0px; padding: 0px; line-height: 1.5;">return</span> <span class="kw2" style="margin: 0px; padding: 0px; line-height: 1.5;">false</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span> <span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span> <span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">void</span> test<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span><span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">int</span> <span class="sy3" style="margin: 0px; padding: 0px; line-height: 1.5;">&&</span> i<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span>    is_r_value<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span>i<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span> <span class="co1" style="margin: 0px; padding: 0px; line-height: 1.5;">// i 為具名變數,即使被宣告成右值也不會被認定是右值。</span>    is_r_value<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span>std<span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">::</span><span class="me2" style="margin: 0px; padding: 0px; line-height: 1.5;">move</span><span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;"><</span><span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">int</span><span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;">></span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span>i<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span> <span class="co1" style="margin: 0px; padding: 0px; line-height: 1.5;">// 使用 std::move<T>() 取得右值。</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span>

由于右值引用的用语特性以及对于左值引用(L-value references;regular references)的某些用语修正,右值引用允许开发者提供完美转发 (perfect function forwarding)。当与变长参数模板结合,这项能力允许函数模板能够完美地转送引数给其他接受这些特定引数的函数。最大的用处在于转送构造函数参数,创造出能够自动为这些特定引数调用正确建构式的工厂函数(factory function)。

泛化的常数表示式

C++ 本来就已具备常数表示式(constant expression)的概念。像是 3+4 总是会产生相同的结果并且没有任何的副作用。常数表示式对编译器来说是优化的机会,编译器时常在编译期运行它们并且将值存入程序中。同样地,在许多场合下,C++ 规格要求使用常数表示式。例如在数组大小的定义上,以及枚举值(enumerator values)都要求必须是常数表示式。

然而,常数表示式总是在遇上了函数调用或是对象建构式时就终结。所以像是以下的例子是不合法的:

<span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">int</span> GetFive<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span> <span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span><span class="kw1" style="margin: 0px; padding: 0px; line-height: 1.5;">return</span> <span class="nu0" style="margin: 0px; padding: 0px; line-height: 1.5;">5</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span> <span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">int</span> some_value<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">[</span>GetFive<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span> <span class="sy2" style="margin: 0px; padding: 0px; line-height: 1.5;">+</span> <span class="nu0" style="margin: 0px; padding: 0px; line-height: 1.5;">5</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">]</span>;<span class="co1" style="margin: 0px; padding: 0px; line-height: 1.5;">// 欲產生 10 個整數的陣列。 不合法的 C++ 寫法</span>

这不是合法的 C++,因为 GetFive() + 5 并不是常数表示式。编译器无从得知 GetFive 实际上在运行期是常数。理论上而言,这个函数可能会影响全局变量,或者调用其他的非运行期(non-runtime)常数函数等。

C++11引进关键字 constexpr 允许用户保证函数或是对象建构式是编译期常数。以上的例子可以被写成像是下面这样:

constexpr <span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">int</span> GetFive<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span> <span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span><span class="kw1" style="margin: 0px; padding: 0px; line-height: 1.5;">return</span> <span class="nu0" style="margin: 0px; padding: 0px; line-height: 1.5;">5</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span> <span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">int</span> some_value<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">[</span>GetFive<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span> <span class="sy2" style="margin: 0px; padding: 0px; line-height: 1.5;">+</span> <span class="nu0" style="margin: 0px; padding: 0px; line-height: 1.5;">5</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">]</span>;<span class="co1" style="margin: 0px; padding: 0px; line-height: 1.5;">// 欲產生 10 個整數的陣列。合法的C++11寫法</span>

这使得编译器能够了解并去验证 GetFive 是个编译期常数。

对函数使用 constexpr 在函数可以做的事上面加上了非常严格的条件。首先,该函数的回返值类型不能为 void。第二点,函数的内容必须依照 "returnexpr" 的形式。第三点,在引数取代后,expr 必须是个常数表示式。这些常数表示式只能够调用其他被定义为 constexpr 的函数,或是其他常数表示式的数据变量。 最后一点,有着这样标签的函数直到在该编译单元内被定义之前是不能够被调用的。

变量也可以被定义为常数表示式值:

constexpr <span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">double</span> forceOfGravity <span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;">=</span> <span class="nu16" style="margin: 0px; padding: 0px; line-height: 1.5;">9.8</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span>constexpr <span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">double</span> moonGravity <span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;">=</span> forceOfGravity <span class="sy2" style="margin: 0px; padding: 0px; line-height: 1.5;">/</span> <span class="nu16" style="margin: 0px; padding: 0px; line-height: 1.5;">6.0</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span>

常数表示式的数据变量是隐式的常数。他们可以只存储常数表示式或常数表示式建构式的结果。

为了从用户自定类型(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兼容的对象布局(object layout)。然而,C++03的规则太严苛了。

C++11将会放宽关于POD的定义。

当class/struct是极简的(trivial)、属于标准布局(standard-layout),以及他的所有非静态(non-static)成员都是POD时,会被视为POD。

一个极简的类型或结构符合以下定义:

  1. 极简的默认建构式。这可以使用默认建构式语法,例如SomeConstructor() = default;
  2. 极简的复制建构式,可使用默认语法(default syntax)
  3. 极简的赋值运算符,可使用默认语法(default syntax)
  4. 极简的解构式,不可以是虚拟的(virtual)

一个标准布局(standard-layout)的类型或结构符合以下定义:

  1. 只有非静态的(non-static)数据成员,且这些成员也是符合标准布局的类型
  2. 对所有non-static成员有相同的访问控制(public, private, protected)
  3. 没有虚函数
  4. 没有虚拟基类
  5. 只有符合标准布局的基类
  6. 没有和第一个定义的non-static成员相同类型的基类
  7. 若非没有带有non-static成员的基类,就是最底层(继承最末位)的类型没有non-static数据成员而且至多一个带有non-static成员的基类。基本上,在该类型的继承体系中只会有一个类型带有non-static成员。

核心语言建构期表现的加强

外部模板

在标准C++中,只要在编译单元内遇到被完整定义的模板,编译器都必须将其实例化(instantiate)。这会大大增加编译时间,特别是模板在许多编译单元内使用相同的参数实例化。看起来没有办法告诉C++不要引发模板的实例化。

C++11将会引入外部模板这一概念。C++已经有了强制编译器在特定位置开始实例化的语法:

<span class="kw2" style="margin: 0px; padding: 0px; line-height: 1.5;">template</span> <span class="kw2" style="margin: 0px; padding: 0px; line-height: 1.5;">class</span> std<span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">::</span><span class="me2" style="margin: 0px; padding: 0px; line-height: 1.5;">vector</span><span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;"><</span>MyClass<span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;">></span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span>

而C++所缺乏的是阻止编译器在某个编译单元内实例化模板的能力。C++11将简单地扩充前文语法如下:

<span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">extern</span> <span class="kw2" style="margin: 0px; padding: 0px; line-height: 1.5;">template</span> <span class="kw2" style="margin: 0px; padding: 0px; line-height: 1.5;">class</span> std<span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">::</span><span class="me2" style="margin: 0px; padding: 0px; line-height: 1.5;">vector</span><span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;"><</span>MyClass<span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;">></span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span>

这样就告诉编译器不要在该编译单元内将该模板实例化。

核心语言使用性的加强

这些特色存在的主要目的是为了使C++能够更容易使用。 举凡可以增进类型安全,减少代码重复,不易误用代码之类的。

初始化列表

标准C++从C带来了初始化列表(initializer list)的概念。这个构想是结构或是数组能够依据成员在该结构内定义的顺序通过给予的一串引数来产生。这些初始化列表是递归的,所以结构的数组或是包含其他结构的结构可以使用它们。这对静态列表或是仅是把结构初始化为某值而言相当有用。C++有构造函数,能够重复对象的初始化。但单单只有那样并不足以取代这项特色的所有机能。在C++03中,只允许在严格遵守POD的定义和限制条件的结构及类型上使用这项机能,非POD的类型不能使用,就连相当有用的STL容器std::vector也不行。

C++11将会把初始化列表的概念绑到类型上,称作std::initializer_list。这允许构造函数或其他函数像参数般地使用初始化列表。举例来说:

<span class="kw2" style="margin: 0px; padding: 0px; line-height: 1.5;">class</span> SequenceClass<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span><span class="kw2" style="margin: 0px; padding: 0px; line-height: 1.5;">public</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">:</span>  SequenceClass<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span>std<span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">::</span><span class="me2" style="margin: 0px; padding: 0px; line-height: 1.5;">initializer_list</span><span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;"><</span><span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">int</span><span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;">></span> list<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span>

这将允许SequenceClass由一连串的整数构造,就像:

SequenceClass someVar <span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;">=</span> <span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span><span class="nu0" style="margin: 0px; padding: 0px; line-height: 1.5;">1</span>, <span class="nu0" style="margin: 0px; padding: 0px; line-height: 1.5;">4</span>, <span class="nu0" style="margin: 0px; padding: 0px; line-height: 1.5;">5</span>, <span class="nu0" style="margin: 0px; padding: 0px; line-height: 1.5;">6</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span>

这个构造函数是种特殊的构造函数,称作初始化列表构造函数。有着这种构造函数的类型在统一初始化的时候会被特别对待。

类型std::initializer_list<>是个第一级的C++11标准程序库类型。然而他们只能够经由C++11编译器通过{}语法的使用被静态地构造 。这个列表一经构造便可复制,虽然这只是copy-by-reference。初始化列表是常数;一旦被创建,其成员均不能被改变,成员中的数据也不能够被变动。

因为初始化列表是真实类型,除了类型构造式之外还能够被用在其他地方。正规的函数能够使用初始化列表作为引数。例如:

<span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">void</span> FunctionName<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span>std<span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">::</span><span class="me2" style="margin: 0px; padding: 0px; line-height: 1.5;">initializer_list</span><span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;"><</span><span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">float</span><span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;">></span> list<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span> FunctionName<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span><span class="nu17" style="margin: 0px; padding: 0px; line-height: 1.5;">1.0f</span>, <span class="sy2" style="margin: 0px; padding: 0px; line-height: 1.5;">-</span><span class="nu17" style="margin: 0px; padding: 0px; line-height: 1.5;">3.45f</span>, <span class="sy2" style="margin: 0px; padding: 0px; line-height: 1.5;">-</span><span class="nu17" style="margin: 0px; padding: 0px; line-height: 1.5;">0.4f</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span>

标准容器也能够以这种方式初始化:

vector<span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;"><</span>string<span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;">></span> v <span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;">=</span> <span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span> <span class="st0" style="margin: 0px; padding: 0px; line-height: 1.5;">"xyzzy"</span>, <span class="st0" style="margin: 0px; padding: 0px; line-height: 1.5;">"plugh"</span>, <span class="st0" style="margin: 0px; padding: 0px; line-height: 1.5;">"abracadabra"</span> <span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span>

统一的初始化

标准 C++ 在初始化类型方面有着许多问题。初始化类型有数种方法,而且交换使用时不会都产生相同结果。传统的建构式语法,看起来像是函数声明,而且为了能使编译器不会弄错必须采取一些步骤。只有集合体和 POD 类型能够被集合式的初始化(使用SomeType var = {/*stuff*/};).

C++11 将会提供一种统一的语法初始化任意的对象,它扩充了初始化串行语法:

<span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">struct</span> BasicStruct<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span> <span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">int</span> x<span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span> <span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">float</span> y<span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span> <span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">struct</span> AltStruct<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span>  AltStruct<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span><span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">int</span> _x, <span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">float</span> _y<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span> <span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">:</span> x<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span>_x<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span>, y<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span>_y<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span> <span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span> <span class="kw2" style="margin: 0px; padding: 0px; line-height: 1.5;">private</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">:</span>  <span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">int</span> x<span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span>  <span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">float</span> y<span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span> BasicStruct var1<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span><span class="nu0" style="margin: 0px; padding: 0px; line-height: 1.5;">5</span>, <span class="nu17" style="margin: 0px; padding: 0px; line-height: 1.5;">3.2f</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span>AltStruct var2<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span><span class="nu0" style="margin: 0px; padding: 0px; line-height: 1.5;">2</span>, <span class="nu17" style="margin: 0px; padding: 0px; line-height: 1.5;">4.3f</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span>

var1 的初始化的运作就如同 C-style 的初始化串行。每个公开的变量将被对应于初始化串行的值给初始化。隐式类型转换会在需要的时候被使用,这里的隐式类型转换不会产生范围缩限 (narrowing)。要是不能够转换,编译便会失败。(范围缩限 (narrowing):转换后的类型无法表示原类型。如将 32-bit 的整数转换为 16-bit 或 8-bit 整数,或是浮点数转换为整数。)var2 的初始化则是简单地调用建构式。

统一的初始化建构能够免除具体指定特定类型的必要:

<span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">struct</span> IdString<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span>  std<span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">::</span><span class="me2" style="margin: 0px; padding: 0px; line-height: 1.5;">string</span> name<span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span>  <span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">int</span> identifier<span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span> IdString var3<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span><span class="st0" style="margin: 0px; padding: 0px; line-height: 1.5;">"SomeName"</span>, <span class="nu0" style="margin: 0px; padding: 0px; line-height: 1.5;">4</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span>

该语法将会使用 const char * 参数初始化 std::string 。你也可以做像下面的事:

IdString GetString<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span>  <span class="kw1" style="margin: 0px; padding: 0px; line-height: 1.5;">return</span> <span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span><span class="st0" style="margin: 0px; padding: 0px; line-height: 1.5;">"SomeName"</span>, <span class="nu0" style="margin: 0px; padding: 0px; line-height: 1.5;">4</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span> <span class="co1" style="margin: 0px; padding: 0px; line-height: 1.5;">// 注意這裡不需要明確的型別</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span>

统一初始化不会取代建构式语法。仍然会有需要用到建构式语法的时候。如果一个类型拥有初始化串行建构式(TypeName(initializer_list<SomeType>);),而初始化串行符合 sequence 建构式的类型,那么它比其他形式的建构式的优先权都来的高。C++11 版本的std::vector 将会有初始化串行建构式。这表示:

std<span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">::</span><span class="me2" style="margin: 0px; padding: 0px; line-height: 1.5;">vector</span><span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;"><</span><span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">int</span><span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;">></span> theVec<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">{</span><span class="nu0" style="margin: 0px; padding: 0px; line-height: 1.5;">4</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">}</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span>

这将会调用初始化串行建构式,而不是调用std::vector只接受一个尺寸参数产生相应尺寸 vector 的建构式。要使用这个建构式,用户必须直接使用标准的建构式语法。

类型推导

在标准 C++(和 C ),使用变量必须明确的指出其类型。然而,随着模版类型的出现以及模板元编程的技巧,某物的类型,特别是函数定义明确的回返类型,就不容易表示。在这样的情况下,将中间结果存储于变量是件困难的事,可能会需要知道特定的元编程程序库的内部情况。

C++11 提供两种方法缓解上述所遇到的困难。首先,有被明确初始化的变量可以使用 auto 关键字。这会依据该初始化子(initializer)的具体类型产生变量:

<span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">auto</span> someStrangeCallableType <span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;">=</span> boost<span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">::</span><span class="me2" style="margin: 0px; padding: 0px; line-height: 1.5;">bind</span><span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">(</span><span class="sy3" style="margin: 0px; padding: 0px; line-height: 1.5;">&</span>SomeFunction, _2, _1, someObject<span class="br0" style="margin: 0px; padding: 0px; line-height: 1.5;">)</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span><span class="kw4" style="margin: 0px; padding: 0px; line-height: 1.5;">auto</span> otherVariable <span class="sy1" style="margin: 0px; padding: 0px; line-height: 1.5;">=</span> <span class="nu0" style="margin: 0px; padding: 0px; line-height: 1.5;">5</span><span class="sy4" style="margin: 0px; padding: 0px; line-height: 1.5;">;</span>

someStrangeCallableType 的类型就是模板函数 boost::bind 对特定引数所回返的类型。作为编译器语义分析责任的一部份,这个类型能够简单地被编译器决定,但用户要通过查看来判断类型就不是那么容易的一件事了。

otherVariable 的类型同样也是定义明确的,但用户很容易就能判别。它是个 int(整数),就和整数字面值的类型一样。

除此之外,decltype 能够被用来在编译期决定一个表示式的�%

0 0
原创粉丝点击