(libstdc++ vs libc++) std::tuple

列表构造顺序

libstdc++ 用的是递归反向构造,尾部的参数先构造。与之对应,libc++ 用的是 parameter pack 继承,是正向构造。

下标编号

两者都使用了下标编号。这样即便 std::tuple 的某两个参数类型相同,也能通过不同的下标把两个元素区分开。

EBCO

两者都有 EBCO 技术。假定 Base<T, bool = InheritableEmptyBase<T>::value> 为两个库所用的元素包装类,Base 在参数为空类且能够被继承时,采用继承的方式存储元素,否则用成员变量存储元素。

但是由于元组参数构造顺序不同,两种库实现占用的空间也可能不同。考虑类型 std::tuple<A, char, A, char, B>,其中 AB 都是空类型,它在 libc++ 中只需要占用 2 个字节。

而 libstdc++ 中同样的类型需要占用 3 个字节:

如图,最后一次继承的时候先继承 TupleImpl<char, A, char, B>(假设 TupleImpl 是递归继承功能的实现模板),后继承 Base<A>,但由于地址 0 处已经有 A 类型,所以只能放在地址 2 处。(EBCO 失效之后只能跨过整个基类,不能摆在基类的中间地址。)

get with index

get 模板的参数中有下标,而特定元组中的下标可以决定其基类的类型。两个库都巧妙利用了基类名唯一的特性,从结果来说是等效的。

libstdc++ 通过限定名获取元素:

_Tuple_impl<__i, _Head, _Tail...>::_M_head(__t);

_M_head 是用来获取被包裹的元素的方法(实际上是个很短的、返回某个数据域的方法)。

libc++ 则将 *this 转换成基类指针再掉用获取元素的方法。