可变参模板、模板模板参数

一、可变参函数模板

// 可变参函数模板
template <typename... T>   // 表示0到多个不同的类型
void MyFun(T... args)    // 表示0到多个不同类型的参数(可以同类型,也可以不同类型)
{
    cout << sizeof...(T) << endl;
    cout << sizeof...(args) << endl;  // 都是表示可变参的个数
}

// 可变参函数模板,参数包的展开
template <typename T, typename... U>   // 
void MyFun2(const T& first, const U&... args)    // 一个参数,后面跟一包参数,这种形式是个参数包展开
{
    cout << "收到的参数为:" << first << endl;
    MyFun2(args...);  // 递归调用,不断获取第一个参数,注意此处参数的写法 args...
}

void MyFun2()   // 解析参数包里面的参数一定要有个无参的终止函数,否则编译报错
{
    cout << "执行递归终止函数" << endl;
}

 

二、可变参类模板

1、通过递归继承方式展开参数包

// 可变参类模板

// 通过递归继承的方式展开参数包
template <typename... T> class MyClass {};  // 可变参类模板的声明

template <>
class MyClass<>  // 该类为 0 参数的特化版本,可写可不写
{  // 有了这个类,下面的可变参模板类会最先构造该类
public:
    MyClass()
    {
        cout << "0参数的特化版本" << endl;
    }
};

template <typename T, typename... U>
class MyClass<T, U...> : private MyClass<U...>  // 偏特化
{
public:
    MyClass() : m_val(0)
    {
        cout << "构造函数 this =" << this << endl;
    }
    MyClass(T first, U... args) : m_val(first), MyClass<U...>(args...)
    {  //带参构造函数
        cout << "m_val = " << m_val << endl;
    }

    T m_val;
};

void MyClassFun()
{
    MyClass<int, double, float> myClass(1, 2.2, 2.3);
    // 
}

执行结构,根据执行结果,可以推断出类的继承顺序:先构造父类,在构造子类

 

 

 

2、通过递归组合方式展开参数包

与继承方式展开很类似,可以对比 1 和 2 中修改的地方

// 通过递归组合的方式展开参数包
template <typename... T> class MyClass {};  // 可变参类模板的声明

template <>
class MyClass<>  // 该类为 0 参数的特化版本,可写可不写
{  // 有了这个类,下面的可变参模板类会最先构造该类
public:
    MyClass()
    {
        cout << "0参数的特化版本" << endl;
    }
};

template <typename T, typename... U>
class MyClass<T, U...> // : private MyClass<U...>  
{
public:
    MyClass() : m_val(0)
    {
        cout << "构造函数 this =" << this << endl;
    }
    MyClass(T first, U... args) : m_val(first), m_other(args...) //MyClass<U...>(args...)
    {  //带参构造函数
        cout << "m_val = " << m_val << endl;
    }

    T m_val;
    MyClass<U...> m_other;  // 组合关系
};

void MyClassFun()
{
    MyClass<int, double, float> myClass(1, 2.2, 2.3);
}

执行的结果与1中相同,可以推断,组合对象先构造

 

 

3、通过tuple和递归展开参数包

这种展开方式需要些类的特化版本

实现思路:计数器从0开始,每处理一个参数,计数器+1,一直到吧所有参数处理完,最后整一个模板偏特化,作为递归调用结束

// 通过tuple和递归展开参数包
// iCount 用于统计第几个参数,maxCount表示一共有多少个参数
template<int iCount, int maxCount, typename... T>
class MyClass
{
public:
    static void StaticFun(const tuple<T...>& t)
    {
        cout << "value = " << get<iCount>(t) << endl;
        MyClass<iCount + 1, maxCount, T...>::StaticFun(t); // 递归调用
    }
};

// 需要一个特化版本,用于结束递归调用
template<int maxCount, typename... T>
class MyClass<maxCount, maxCount, T...>
{
public:
    static void StaticFun(const tuple<T...>& t)
    {
        cout << "特化版本" << endl;
    }
};

// 通过一个模板函数来调用tuple参数类型
template <typename... T>
void MyTemplateFun(const tuple<T...>& t)  // 可变参函数模板
{
    MyClass<0, sizeof...(T), T...>::StaticFun(t);
}

void MyClassFun()
{
    tuple<int, double, float> myTuple(1, 2.2, 2.3);
    // tuple 元组,里面可以是任意类型参数的组合
    //cout << get<0>(myTuple) << endl; // 取元组中的第一个元素
    MyTemplateFun(myTuple);
}

执行结果如下,可以推断出函数调用的顺序

 

 

 

小结:参数包的展开一般都是采用递归的方法

三、模板模板参数

 即 可以作为模板的模板参数

// 模板 模板参数:本身是一个模板参数,这个模板参数同时又是模板
//template <typename T,template<class> class U >
template <
    typename T,   // 模板参数
    template<class> class U   // 模板模板参数,一般作用是作为一个容器
    // template<typename W> typename U   // 这与上一种写法意思是一样的,但稍有复杂
    // 其中的 W 并没有任何用处,只是占位
>
class MyClass
{
public:
    MyClass()
    {
        for (int i = 0; i < 10; ++i)
        {
            m_Uval.push_back(i);
            // 该构造函数是否能编译过,取决于传入的容器是否支持push_back操作
        }
    }

public:
    T m_val;
    U<T> m_Uval;  // U相当于一个类模板
    // 所以如果想通过 U<T> 的方式来使用U,就必须将U当作一个模板模板参数
};

template<typename T>
using MyVector = vector<T, allocator<T>>;  // 常用写法,来定义模板别名

int main()
{
    //MyClass<int, vector> myVector;  // 直接传vector 编译会报错,因为分配器不知道如何分配
    MyClass<int, MyVector> myVector;

    system("pause");
    return 0;
}

 

posted @ 2020-06-21 21:01  min_zhi  阅读(300)  评论(0编辑  收藏  举报