C++  Variadic Templates 应用在 STL 的方方面面,可变参数的函数模板、类模板的核心工作机制是参数包的展开。C++17 之前通过递归的形式展开参数包,递归过程就是:将 N个元素的参数包分离第一个出来,将剩余的 N-1 个元素作为第2包,按照第1包过程继续分离第2包的一个元素,剩下 N-2 个元素作为第3包继续分离……这样不断重复下去,结果就是分离出最后一个且剩下一个空包,整个包就完全展开了。为了实现分离包的第一个元素,通常将模板参数列表定义为1个模板参数+1个可变参数的形式,如下 tuple 的模板参数列表:

recursive_inherited.hpp 定义了 tuple:

#pragma once

namespace 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 的核心机制,应该讲的比较细致,如有错误欢迎指正。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐