欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++11类型系统

程序员文章站 2022-07-12 15:53:55
...

C++ feels like a new language. -- Bjarne Stroustrup

  1. 类型推演
  2. 右值引用
  3. 通用引用
  4. 剖析std::move
  5. 剖析std::forward
  6. 总结

类型推演

增强了的「类型系统」是C++11最大的优化亮点之一,为此需要深入剖析「类型推演」的工作机理,并能灵活地运用auto, decltype,这是C++11最重要的基石。

template与auto

C++98早已具备类型推演的能力,用于模板的类型推演。

C++11中,autotemplate的类型推演能力基本类似,只存在唯一的差异:Braced Initialization,或称为Universal Initialization

非常量的左值引用

需要注意的是,推演auto &r2 = r, auto &cr2 = cr时,即使r(int&), cr(const int&)是引用变量,需要去除引用后再尝试类型推演,因为使用「引用变量」等价于使用其「引用对象」本身。

常量的左值引用

因为const T&const auto&已经具备了const的属性,当const的左值对象赋予它所发生的自动类型推演,其模板参数T,及其auto的类型无需推演为const属性。


指向非常量的指针

指针的推演能力与引用类似。

需要注意auto *p = &i; auto p = &i两种写法的不一样,一种是显式的指针类型,另外一种完全依赖于auto的类型推演能力。

指向常量的指针

与指向非常量的指针推演机制一致,在此不再冗述。


按值传递

Pass-By-Value,经过拷贝之后,两者之间已无任何瓜葛,为此const的处理机制有别于其他情况。



但存在两类特殊的,遗留的C-Style情况,为保证兼容性,存在特殊的类型推演机制。

遗留的C-style字符串

遗留的C-style函数

通用引用:Universal Reference

所谓Universal Reference,因为其能Can bind to anything,所以称为「通用引用」,具有如下方面的特点:

  • Can bind to lvalue or rvalue;
  • Can bind to const/non-const, volatile/non-volatile, or both;
  • So, it can bind to anything.

需要注意的是,Universal Reference并非「右值引用(Rvalue Reference)」,即使它们两者都有类似的T &&的修饰符。规则非常简单,Universal Reference具备两个最基本的特征:

  • T &&, auto&&: 必须具备的句法结构
  • type reduce:必须发生类型推演

可以简单归纳之,Universal Reference出现于如下两种常见:

template <typename T>
void f(T&& t);

auto&& r = i;

Universal Reference类型推演也存在特殊性:

  • Universal Reference持有左值时,发生Reference Collapsing机制。例如auto&& t = i,当auto推演为int&auto&& t推演为int& && t,而int& &&经过Reference Collapsing机制,被进一步规约为int&,与原来它持有左值刚好匹配。

  • Universal Reference持有右值时,推演规则较为直观,例如auto&& t = 10,当auto被推演为int,则auto&& t推演为int&& t,与原来它持有右值刚好匹配。



通用初始化:Braced Initialization

这是templateauto类型推演能力之间存在的唯一差别。

右值引用

「右值引用」(Rvalue Reference) 与「通用引用」(Universal Reference)是两个不同的概念,非常容易混淆,本文试图揭示两者之间的本质的差异。

左值与右值

「左值」与「右值」并非C++11的产物,早已是C++类型系统的一部分了,并且两者之间存在明显的区别。

举个例子,进一步明细两者之间的差异。此处使用auto&&Universal Reference,它会根据「左值」自动推演为「左值引用」,而「右值」推演为「右值引用」。

右值引用

C++98中,只存在「左值引用」,遗恨缺失「右值引用」的概念,也因此丢失了部分性能优化的空间。C++11中引入了「右值引用」,弥补之前的过失,结合「移动」(move)的机制,进一步提高了C++在特殊场景的性能。

所谓右值引用,即「右值」的引用;之前惯称的「引用」,其实是「左值引用」的简称。「左值引用」只能引用「左值」,「右值引用」只能引用「右值」。

通用引用

「通用引用」并非「左值引用」,即使它们之间都具有&&的语法结构。「通用引用」即可以持有「左值」,也可以持有「右值」,是一种「通用」的引用类型。而「右值引用」只能引用「右值」。

特征

  • void f(Object&& o),因为未发生类型推演,为右值引用
  • template <typename T> void f(std::vector<T>&& v),因为不是T&&的句法结构,为右值引用

可以简单归纳之,「通用引用」出现于如下两种常见:

template <typename T>
void f(T&& t);

auto&& r = i;

样例

std::vector新增加的「右值引用」的push_back,及其「通用引用」的emplace_back是最好的案例。

剖析std::move

C++11实现

当传递左值时

经过如下的类型推演过程,当传递「左值」时,std::move强制转为换「右值引用」。

当传递右值时

经过如下的类型推演过程,当传递「右值」时,std::move顺水推舟,传递「右值引用」。综上述,借助「通用引用」的能力,std::move其实完成了「无条件的」右值引用转换规则。

C++14改进实现

剖析std::forward

C++11实现

当传递左值时

经过如下的类型推演过程得知,当传递「左值」时,std::forward完成「左值」的「转发」机制。

当传递右值时

经过如下的类型推演过程得知,当传递「右值」时,std::forward也完成「右值」的「转发」机制。为此,std::forward的机制,完成了C++11的「完美转换」(Perfect Forward)的机制。

C++14改进实现

习惯用法

回顾