1、pressed_pair 源码剖析意义当 compressed_pair 的某一个模板参数为一个空类的时候将对其进行“空基类优化”,这样可以使得 compressed_pair 占用的空间比 std:pair 的更小。参考如下代码:#include using namespace std;#include class A;class B;int main() cout ) ) struct pair / store a pair of valuestypedef pair _Myt;typedef _Ty1 first_type;typedef _Ty2 second_type;pair():
2、 first(_Ty1(), second(_Ty2() / construct from defaultspair(const _Ty1 / the first stored value_Ty2 second; / the second stored value;通过直接访问 first 和 second 对数据进行访问。pressed_pair 参考pressed_pair 大概的实现如下:class compressed_pairpublic:typedef T first_type;typedef T second_type;typedef typename call_traits:p
3、aram_type first_param_type;typedef typename call_traits:param_type second_param_type;typedef typename call_traits:reference first_reference;typedef typename call_traits:reference second_reference;typedef typename call_traits:const_reference first_const_reference;typedef typename call_traits:const_re
4、ference second_const_reference;first_reference first() return base:first();first_const_reference first() const return base:first();second_reference second() return base:second();second_const_reference second() const return base:second();void swap(:boost:compressed_pair ;注意这不是完整的代码,它只是对其实现的一个简单描述。从中我
5、们可以看出 pressed_pair 使用成员函数来访问数据而不是如std:pair 一样直接访问 first 和 second。pressed_pair 剖析pressed_pair 的实现依赖于 boost.type_traits 和 boost.call_traits。boost.type_traits 是 boost 提供的一个特征类库,这是一个强大的库,可以应用于很多地方。boost 的大量组件都依赖于它。boost.call_traits 也是一个类似于 type_traits 的库,它主要提供的是一些类型调整,通过编译器演绎我们可以在编译时得到最好的 type,它可以使我们的传递
6、的参数等等相关内容总是以最恰当(根据经验)的方式来进行调用,而且还能在新的 C+标准发布之前绕过“引用的引用”问题。接下来我将剖析支持偏特化版本的 compressed_pair 的实现,它位于 boostdetailcompressed_pair.hpp。compressed_pair_switch这是一个开关工具,用于在后面对各种情况进行开关控制,它的基本实现如下:template struct compressed_pair_switch;注意,它只是定义而非实现,因此我们无法构造未特化过的 compressed_pair_switch。通过查看它的模板参数可以知道后面三个 bool 代
7、表了三个概念: pair 的两个模板参数是否是相同类型。 (去掉 cv 限定符之后) 。 第一个模板参数是空的吗? 第二个模板参数是空的吗?因此对这三个 bool 进行有限组合可以得到 6 种组合,也就出现了接下来我们所看到的 6 个特化(偏特化) 。template struct compressed_pair_switchstatic const int value = 0;template struct compressed_pair_switchstatic const int value = 3;template struct compressed_pair_switchstatic
8、 const int value = 1;template struct compressed_pair_switchstatic const int value = 2;template struct compressed_pair_switchstatic const int value = 4;template struct compressed_pair_switchstatic const int value = 5;现在我们已经偏特化了 6 个不同的开关,它们将在最终实现 compressed_pair 的过程中发挥巨大的作用。注意每个类中的 value,这个常量值代表了它的版本。
9、compressed_pair_imp它作为最终 compressed_pair 的基类存在,它的声明如下:template class compressed_pair_imp;注意第三个参数 Version,在最终的实现中它将被以 compressed_pair_switch:value 来具现化。接下来按照 compressed_pair_switch 的 6 种版本所说明的 6 中组合情况分别实现其对应的 compressed_pair_imp。在文章最开始的时候我们的简单程序发现 std:pair 由于直接采用组合 T1、T2 而无法使之成功的应用“空基类优化 ”,使得其占用空间的大小
10、是 2.如果 compressed_pair_imp 也直接按照这种组合来实现的话,那么所谓“压缩”便不会有任何意义。所以compressed_pair_imp 对应不同组合情况有不同的实现,比如说对于版本 1:template struct compressed_pair_switchstatic const int value = 1;这种情况便是指 T1 和 T2 在去 cv 限定符之后为不同类型,且第一种类型为空,第二种不为空,那么这时候在实现compressed_pair_imp 的时候便取消了 T1 的数据,源码如下:template class compressed_pair_i
11、mp: protected :boost:remove_cv:typepublic:typedef T1 first_type;typedef T2 second_type;typedef typename call_traits:param_type first_param_type;typedef typename call_traits:param_type second_param_type;typedef typename call_traits:reference first_reference;typedef typename call_traits:reference seco
12、nd_reference;typedef typename call_traits:const_reference first_const_reference;typedef typename call_traits:const_reference second_const_reference;compressed_pair_imp() compressed_pair_imp(first_param_type x, second_param_type y): first_type(x), second_(y) compressed_pair_imp(first_param_type x): f
13、irst_type(x) compressed_pair_imp(second_param_type y): second_(y) first_reference first() return *this;first_const_reference first() const return *this;second_reference second() return second_;second_const_reference second() const return second_;private:second_type second_;现在回过头去,对于版本 0:template str
14、uct compressed_pair_switchstatic const int value = 0;由于 T1 和 t2 为不同类型,同时都不为空,因此这种情况下 compressed_pair 与 std:pair 是一样的。对于版本 2:template struct compressed_pair_switchstatic const int value = 2;T1 和 T2 不相同,且 T1 不为空,T2 为空,那么这和版本 1 的差别就在于 t2 的数据成员被取消,T1 的数据成员存在。template class compressed_pair_imp: protected
15、 :boost:remove_cv:typefirst_reference first() return first_;first_const_reference first() const return first_;second_reference second() return *this;second_const_reference second() const return *this;private:first_type first_; / 删除了某系无关紧要的代码版本 3:template struct compressed_pair_switchstatic const int
16、 value = 3;T1 和 t2 不相同,且两者均为空。这种时候 compressd_pair_imp 不再需要任何数据成员,因此其精简版的定义如下 :template class compressed_pair_imp: protected :boost:remove_cv:type,protected :boost:remove_cv:typefirst_reference first() return *this;first_const_reference first() const return *this;second_reference second() return *thi
17、s;second_const_reference second() const return *this;/ no need to swap empty bases:void swap(:boost:compressed_pair在这里面我们可以看到它的交换动作根本什么也没做。而且也没有数据成员,但是其占用空间大小依然是 1.版本 4 定义了 T1 和 T2 相同,且均为空的特殊情况:template struct compressed_pair_switchstatic const int value = 4;template class compressed_pair_imp: prote
18、cted :boost:remove_cv:typefirst_reference first() return *this;first_const_reference first() const return *this;second_reference second() return m_second;second_const_reference second() const return m_second;void swap(:boost:compressed_pair;既然 T1 和 T2 均为空,那么为何还要保存一个 T2 的数据呢?这是为了防止 first()和 second()所
19、返回的对象的地址相同,这是很郁闷的一件事情。版本 5 定义了 T1 和 T2 相同,且均不为空的情况:template class compressed_pair_impfirst_reference first() return first_;first_const_reference first() const return first_;second_reference second() return second_;second_const_reference second() const return second_;void swap(:boost:compressed_pairc
20、p_swap(second_, y.second();private:first_type first_;second_type second_;这个版本并没有什么特殊之处。compressed_pair最终的实现通过继承 compressed_pair_imp 来实现,而上述的 compressed_pair_imp 都有一个 Version 模板参数,通过编译时推断出的 compressed_pair_switch 的数据 value 则可得到与其对应的基类。template class compressed_pair: private :boost:details:compressed_
21、pair_imp:type, typename remove_cv:type:value,:boost:is_empty:value,:boost:is_empty:value:valuetemplate class compressed_pair: private details:compressed_pair_imp:type, typename remove_cv:type:value,:boost:is_empty:value,:boost:is_empty:value:value这是按照 T1 和 T2 是否相同所不同的实现。它们的区别主要在构造函数的实现上:explicit com
22、pressed_pair(first_param_type x) : base(x) explicit compressed_pair(second_param_type y) : base(y) 对于 T1 和 T2 相同的情况,上面的这段代码不是合法的重载。:boost:is_same 将对 T1 和 T2 去 cv 限定符之后的类型进行比较,结果 value 是一个 bool 值常量,对应于compressed_pair_switch 第三个模板参数。:boost:is_empty 可以判断类型是否是空类型,其值 value 也是一个 bool 值常量。这样便可推断出该继承哪一个版本的
23、compressed_pair_imp。其它重点这里将继续探讨一些有意义的技巧。继承在这里我们将聚焦 compressed_pair_imp 的实现,其中除了第 0 版本和第 5 版本之外,其它的实现均不同程度的使用了继承,比如第 1 版:template class compressed_pair_imp: protected :boost:remove_cv:type这个版本是:template struct compressed_pair_switchstatic const int value = 1;即 T1 和 T2 不同,且只有 T1 为空,那么它继承去 cv 限定符之后的 T1
24、 类型。它的意义何在呢?参考第 1 版本的 compressed_pair_imp 的实现可以看到:first_reference first() return *this;first_const_reference first() const return *this;当需求要 T1 的类型对象的时候,直接返回的是 compressed_pair_imp 对象自己,如果不继承的话这个动作将是非法的。这就是为什么有这样一个继承的原因。对于版本 3:template class compressed_pair_imp: protected :boost:remove_cv:type,protec
25、ted :boost:remove_cv:type还可以看到:first_reference first() return *this;first_const_reference first() const return *this;second_reference second() return *this;second_const_reference second() const return *this;实际上道理是一样的。同时它还解决了构造函数中的这个问题:compressed_pair_imp(first_param_type x, second_param_type y): first_type(x), second_type(y) 因为 first_type、second_type 为其基类,这样的调用才合法。还有一个猜想就是如果 T1 是内置类型的话,比如说 int,那么继承 int 会是合法的代码吗?实际上我们并不需要担心这个,因为 int 不会通过:boost:is_empty 测试。 boost:is_empty:value 将得到 false,因此不会被编译器演绎到这一步。
Copyright © 2018-2021 Wenke99.com All rights reserved
工信部备案号:浙ICP备20026746号-2
公安局备案号:浙公网安备33038302330469号
本站为C2C交文档易平台,即用户上传的文档直接卖给下载用户,本站只是网络服务中间平台,所有原创文档下载所得归上传人所有,若您发现上传作品侵犯了您的权利,请立刻联系网站客服并提供证据,平台将在3个工作日内予以改正。