C++ 可变参数模板实现递归继承(tuple 实现原理)
C++ Variadic Templates 应用在 STL 的方方面面,可变参数的函数模板、类模板的核心工作机制是参数包的展开。C++17 之前通过递归的形式展开参数包,递归过程就是:将 N个元素的参数包分离第一个出来,将剩余的 N-1 个元素作为第2包,按照第1包过程继续分离第2包的一个元素,剩下 N-2 个元素作为第3包继续分离……这样不断重复下去,结果就是分离出最后一个且剩下一个空包,整个包就完全展开了。为了实现分离包的第一个元素,通常将模板参数列表定义为1个模板参数+1个可变参数的形式,如下 tuple 的模板参数列表:
recursive_inherited.hpp 定义了 tuple:
#pragma oncenamespace ns_yao {// [0] 类模板前置声明
template<class... _Tys>class tuple;// [1] 空参数包特化版本,最后应用于继承链的最顶层,就上边说到的参数包分离到最后剩一个
// 空包时让编译器使用
template<>class tuple<> {};// [2] tuple 形式像继承了自己,其实不然因为它们的模板参数列表不一样。
// 当调用方实例化类模板时会提供模板参数列表和函数参数列表,并将模板参数包进行分离:第一个模板
// 参数指明子类元素类型,剩余模板参数构成父类的模板参数包。父类的参数包同样要展开去实例化类模板
// 这样就形参递归,最后形成下边代码片段所示的类继承关系。
template<class _Head, typename... _Others>
class tuple<_Head, _Others...> : private tuple<_Others...>
{typedef tuple<_Others...> parent;public:tuple() {}// [3] 构造函数参数包展开,因为创建 tuple 对象时所有实参类型和模板参数类型是一一对应的,// 所以分离剩余的参数包传给父类构造函数tuple(_Head v, _Others... tails) : parent(tails...), m_head(v) {}public:_Head head() { return m_head;}parent &inheritance() {return *this;}private:_Head m_head;
};
} // ns_yao
my_tuple_test.cpp 对 tuple 模板进行测试,给出了head() 、inheritance() 函数调用的等效代码以及类继承关系,最后还给出对象的内存模型:
#include "recursive_inherited.hpp"#include <bitset>
#include <string>
#include <iostream>int main(int argc, char* argv[])
{using std::endl;// [4]ns_yao::tuple<int, float, std::bitset<8>, std::string> tuple_v(88, .999f, -4, "hello typle");// [5]std::cout << tuple_v.head() << endl<< tuple_v.inheritance().head() << endl<< tuple_v.inheritance().inheritance().head() << endl<< tuple_v.inheritance().inheritance().inheritance().head() << endl;return 0;
}
// 基类引用绑定到派生类对象
// [5] inheritance() 函数的本质就是将 this 绑定到父类型引用上,实现静态绑定
inline ns_yao::tuple<float, std::bitset<8UL>, std::string> &ns_yao::tuple<int, float, std::bitset<8UL>, std::string>::inheritance() //
inline ns_yao::tuple<std::bitset<8UL>, std::string> &ns_yao::tuple<float, std::bitset<8UL>, std::string>::inheritance() //
inline ns_yao::tuple<std::string> &ns_yao::tuple<std::bitset<8UL>, std::string>::inheritance() ////[5] 非虚函数调用采用静态绑定(调用基类版本)
inline int ns_yao::tuple<int, float, std::bitset<8UL>, std::string>::head()
inline float ns_yao::tuple<float, std::bitset<8UL>, std::string>::head()
inline std::bitset<8UL> ns_yao::tuple<std::bitset<8UL>, std::string>::head()
inline std::string ns_yao::tuple<std::string>::head()
*/[6] 继承关系:+-----------------------------------------------------------+| ns_yao::tuple<> |+-----------------------------------------------------------+/|\|+-----------------------------------------------------------+| ns_yao::tuple<std::string> ||-----------------------------------------------------------|| std::string m_head; |+-----------------------------------------------------------+/|\|+-----------------------------------------------------------+| ns_yao::tuple<std::bitset<8UL>, std::string> ||-----------------------------------------------------------|| std::bitset<8UL> m_head; |+-----------------------------------------------------------+/|\|+-----------------------------------------------------------+| ns_yao::tuple<float, std::bitset<8UL>, std::string> ||-----------------------------------------------------------|| float m_head; |+-----------------------------------------------------------+ /|\|+-----------------------------------------------------------+| ns_yao::tuple<int, float, std::bitset<8>, std::string> ||-----------------------------------------------------------|| int m_head; |+-----------------------------------------------------------+ [7] 内存布局:+---------------------------+| 88 ||---------------------------|| .999f ||---------------------------|| 0b11111100 ||---------------------------| 0x30150 + sizeof(string)| "hello typle" |+---------------------------+ 0x30150(this)
以上就是 tuple 的核心机制,应该讲的比较细致,如有错误欢迎指正。
