std::tuple 实现原理以及反序存储

作者: , 共 1670 字 , 共阅读 0

std::tuple的原理并不复杂,但有些细节非常有意思。其中有一个是至少在gnu C++ std的实现中,std::tuple是倒序存储的:

std::tuple<int, int> ii;
std::cout << (&std::get<0>(ii)) < (&std::get<1>(ii)) << std::endl;  // output false

很多人都以为它就是下面这样简单实现的:

template<typename Head, ...typename Tail>
my::tuple : private my::tuple<...Tail> {
    Head head;
};

但这个实现有个问题,空类也会占用一个字节的空间!这和std::tuple的实际表现不符。具体可以参考下面代码:

struct Empty { };

int main() {
  std::cout << sizeof(std::tuple<double, Empty>) << std::endl;  // 8
  std::cout << sizeof(my::tuple<double, Empty>) << std::endl;  // 16
} 

为了达到省去空类的占用空间,std::tuple的所有成员都是继承出来的。这利用了 C++的一个特性:继承空类不会增加子类的空间。

template<std::size_t _Idx, typename _Head, typename... _Tail>
struct _Tuple_impl<_Idx, _Head, _Tail...> 
    : public _Tuple_impl<_Idx + 1, _Tail...>, private _Head_base<_Idx, _Head>
{ }

template<typename... _Elements>
class tuple : public _Tuple_impl<0, _Elements...> 
{ }

也就是说std::tuple<int, double, char>基本相当于:

std::tuple<int, double, char>
    : private _Head_base<2, char>, private _Head_base<1, double>, private _Head_base<0, int>
{}

_Head_base只是对元素的一个简单封装:

template<std::size_t _Idx, typename _Head>
struct _Head_base<_Idx, _Head, false> {
    _Head value;
};

但它对空类做了一个特殊处理,使得封装后还是空类(注意上面的实现并没有得到空类):

template<std::size_t _Idx, typename _Head, bool = __empty_not_final<_Head>::value>
struct _Head_base;

// 此时 _Head 为空类。
template<std::size_t _Idx, typename _Head>
struct _Head_base<_Idx, _Head, true> : public _Head
{ }

由此可见,std::tuple的确是倒序存储的。但为什么它要这么做呢?【待续】

Q. E. D.

类似文章:
编程 » C++, 智能指针
理论上而言,当 C++提供了std::unique_ptr, C++的程序就不应该出现普通指针了。所有普通指针都可以用std::unique_ptr代替,避免手动删除对象。
编程 » C++, 异步
C++11 的标准异步库至少包含下面内容:
编程 » C++, 编译错误
在 gcc 中,存在继承关系的模版类,子类无法直接访问父类的成员,即使该成员是protectedpublic
编程 » C++, 智能指针
前面已经提到std::shared_ptr有三个缺陷:
智能指针在现代 C++里用得越多。以前只知道它大致的原理,比如使用引用计数。但很多实现细节并不清楚,最直接的,它是如何实现多线程安全的?今天找了 gnu c++ library 的实现好好看了一下。
std::thread是 C++ 11 新引入的标准线程库。在同样是 C++ 11 新引入的 lambda 函数的辅助下,std::thread用起来特别方便:
编程 » GIT
git经常出现类似于HEAD detached at origin/development的错误提示。这是因为当前的 HEAD 没有位于任何一个分支上的缘故。
编程 » C++, Boost, 智能指针
如果理解了侵入式容器,侵入式智能指针也很容易理解。传统的智能指针std::shared_ptr使用了和数据无关的引用计数,这带来两个问题:
编程 » folly, C++, 数据容器
由 Facebook 开发和维护的 C++库 Folly 提供folly::small_vector,代码文件地址:https://github.com/facebook/folly/blob/master/folly/small_vector.h
C++对一个有序序列[first, last)firstlast都是iterator,可简单理解为位置指针),以及指定值v,标准库直接提供二分查找的函数std::lower_boundstd::upper_bould
编程 » C++, 智能指针
理论上而言,当 C++提供了std::unique_ptr, C++的程序就不应该出现普通指针了。所有普通指针都可以用std::unique_ptr代替,避免手动删除对象。
由 Facebook 开发和维护的 C++库 Folly 提供了锁folly::MicroLock,代码文件地址:https://github.com/facebook/folly/blob/master/folly/MicroLock.h