std::tuple 是 C++ 标准库中的一个模板类,它用于存储固定大小的、类型可能不同的值的集合。与数组或标准库中的其他序列容器(如 std::vector、std::list)不同,std::tuple 可以包含不同类型的元素,并且它的大小在编译时是固定的。
我们曾经在《Modern C++ std::tuple的size》中提出一个sizeof(std::tuple)的问题,不过要弄懂这个问题需要两步:先理解tuple的基本数据结构,再弄懂一些关于存储的优化。本节着重第一步。
要理解tuple的实现,可以读源码,在我机器上源码在/usr/include/c++/8/tuple。但现代人就要用现代化工具,我想先用chatGPT了解下大概,再用GDB打印一个tuple实例的继承体系。
Chat GPT还贴心的给了一个简单的实现帮我们理解:
// Simplified Tuple Implementation (Conceptual)
template <typename... Types>
class Tuple;
template <>
class Tuple<> {}; // Base case for an empty tuple
template <typename Head, typename... Tail>
class Tuple<Head, Tail...> {
public:
Tuple(Head head, Tail... tail) : head_(head), tail_(tail...) {}
// Access functions
Head& getHead() { return head_; }
const Head& getHead() const { return head_; }
Tuple<Tail...>& getTail() { return tail_; }
const Tuple<Tail...>& getTail() const { return tail_; }
private:
Head head_;
Tuple<Tail...> tail_;
};
// Example usage:
int main() {
Tuple<int, double, char> myTuple(42, 3.14, 'A');
std::cout << myTuple.getHead() << std::endl; // Accessing the first element
std::cout << myTuple.getTail().getHead() << std::endl; // Accessing the second element
return 0;
}
先写个简单的CPP程序
#include
using namespace std;
int main(){
std::tuple<char, int,double> t = {'a', 1, 2.2};
}
编译后GDB调试过std::tuple那一行。
打印t
(gdb) p t
$1 = std::tuple containing = {[1] = 97 'a', [2] = 1, [3] = 2.2000000000000002}
GDB隐藏了具体的数据结构,只打印了程序员关心的数值。是时候启用命令ptype了:
ptype 是 GNU Debugger (GDB) 中的一个命令,用于打印出数据类型的信息。当你在调试过程中,需要了解某个变量、表达式或者类型的数据结构时,ptype 命令会非常有用。
(gdb) ptype t
type = class std::tuple<char, int, double> : public std::_Tuple_impl<0, char, int, double> {
public:
tuple(const std::tuple<char, int, double> &);
tuple(std::tuple<char, int, double> &&);
std::tuple<char, int, double> & operator=(const std::tuple<char, int, double> &);
std::tuple<char, int, double> & operator=(std::tuple<char, int, double> &&);
void swap(std::tuple<char, int, double> &);
}
可见tuple继承自std::_Tuple_impl<0, char, int, double>,继续ptype std::_Tuple_impl<0, char, int, double>:
(gdb) ptype std::_Tuple_impl<0, char, int, double>
type = struct std::_Tuple_impl<0, char, int, double> : public std::_Tuple_impl<1, int, double>
, private std::_Head_base<0, char, false> {
public:
...
}
std::_Tuple_impl<0, char, int, double>又继承自两个类:std::_Tuple_impl<1, int, double>,std::_Head_base<0, char, false>。一路ptype下去,我们就会得到完整的继承图。
链式继承,且每一层都有一个数据_M_head_Impl, 就像洋葱一样一层层的,每层的数据都能打印出来:
(gdb) p t.std::_Tuple_impl<0, char, int, double>::_M_head_impl
$9 = 97 'a'
(gdb) p t.std::_Tuple_impl<1, int, double>::_M_head_impl
$10 = 1
(gdb) p t.std::_Tuple_impl<2, double>::_M_head_impl
$11 = 2.2000000000000002
好了,这就是std::tuple的庐山真面目,是不是学的很快?
最后,不知道有人注意到上面类模板参数中有很多false/true没有?这些就是sizeof(tuple)变小的答案,我们下节见~