C++11类型系统

作者: 刘光聪 | 来源:发表于2016-09-28 10:56 被阅读591次

    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改进实现

    习惯用法

    回顾

    相关文章

      网友评论

        本文标题:C++11类型系统

        本文链接:https://www.haomeiwen.com/subject/evmhyttx.html