TGP 模板基础知识--04可变参数模板

可变参数模板

允许模板定义中包含0至任意多个模板参数

可变参函数模板

#include <iostream>
// 可变参函数模板,
// ...表示参数包,T可变参类型,
// args:称为一包参数(0~多个),每个参数的类型可以各不相同
template <typename... T>
void func(T... args)  // T:包类型, args:包参数
{
    std::cout << "sizeof...(args):" << sizeof...(args) << std::endl;  // 参数数量
    std::cout << "sizeof...(T):" << sizeof...(T) << std::endl;  // 类型数量
}

int main(int argc, char** argv)
{
    func();  // 0
    func(1, "a", 3, 'b', 5, 6);  // 6
    return 0;
}

对于可变参函数模板传递进来的实参一般采用递归函数的方式展开参数包,要展开,要求在可变参函数模板代码中有一个参数包展开函数 + 一个同名的递归终止函数

#include <iostream>
// 递归终止函数,位于参数包展开函数前面
void func()
{
    std::cout << "the end" << std::endl;
}

// 参数包展开函数
template <typename T, typename... U>
void func(T first, U... others)
{
    std::cout << "typename:" << typeid(first).name() << " value:" << first << std::endl;
    func(others...);  // 递归调用,传入的是一包参数 ... 不能省略
}

int main(int argc, char** argv)
{
    func(1, "a", 3, 'b', 5, 6);  // 6
    func();
    return 0;
}

重载

#include <iostream>

template <typename... T>
void func(T... args)
{
    std::cout << "func(T... args) run" << std::endl;
}

template <typename... T>
void func(T*... args)
{
    std::cout << "func(T*... args) run" << std::endl;
}

void func(int arg)
{
    std::cout << "func(int arg) run" << std::endl;
}

int main(int argc, char** argv)
{
    func(1);  // func(int arg)
    func(1, "a", 3, 'b', 5, 6);  // func(T... args)
    func(nullptr);  // func(T... args)
    func((int*)nullptr);  // func(T*... args)
    return 0;
}

折叠表达式

目的:计算某个值(表达式的结果是一个值),该值的特殊性在于它与所有可变参数有关,而不是与单独某个可变参有关,需要所有可变参都参与计算才能求出该值。折叠表达式有四种格式:一元左折一元右折二元左折二元右折
注意:每种格式的折叠表达式都需要用圆括号括住
左折:参数从左侧开始计算
右折:参数从右侧开始计算

#include <iostream>
#include <string>

template <typename... T>
auto add_val(T... args)
{
    return (... + args);  // ((arg1 + arg2) + arg3) ...
}

int main(int argc, char** argv)
{
    auto result = add_val(47, 11, 81, -1);
    std::cout << result << std::endl;
    return 0;
}
一元左折(unary left fold)

格式: (… 运算符 一包参数)
计算方式:(((参数1 运算符 参数2) 运算符 参数3) … 运算符 参数N)

template <typename... T>
auto sub_unary_left_fold(T... args)
{
    return (... - args);
}

// (((10 - 20) - 30) - 40) = -80
auto left_res  = sub_unary_left_fold(10, 20, 30, 40); 
一元右折(unary right fold)

格式: (一包参数 运算符 …)
计算方式:(参数1 运算符 (…(参数N-2 运算符 (参数N-1 运算符 参数N))))

template <typename... T>
auto sub_unary_right_fold(T... args)
{
    return (args - ...);
}

// (10 - (20 - (30 - 40))) = -20
auto right_res = sub_unary_right_fold(10, 20, 30, 40);
二元左折(binary left fold)

格式: (init 运算符 … 一包参数)
计算方式:(((init 运算符 参数1) 运算符 参数2) … 运算符 参数N)
init: 一个初始值,也可以是其他东西

template <typename... T>
auto sub_binary_left_fold(T... args)
{   
    return (220 - ... - args); // init:220, 运算符:-
}

template <typename... T>
void print_binary_left_fold(T... args)
{   
    (std::cout << ... << args); // init:std::cout 运算符:<<
}

// ((((220 - 10) -20) - 30) - 40) = 120
auto left_res2  = sub_binary_left_fold(10, 20, 30, 40);
print_binary_left_fold("hello", " ", "world", " ", "!");
二元右折(binary right fold)

格式: (一包参数 运算符 … 运算符 init)
计算方式:(参数1 运算符 (… (参数N 运算符 init)))

template <typename... T>
auto sub_binary_right_fold(T... args)
{
    return (args - ... - 220); // init:220 运算符:-
}

// (10 - (20 - (30 - (40 - 220)))) = 200
auto right_res2  = sub_binary_right_fold(10, 20, 30, 40);

可变参类模板

允许模板定义中包含**0或多个(任意个)**模板参数

通过递归继承方式展开类型、非类型、模板模板参数包
类型模板参数包展开示例
#include <iostream>
#include <string>

// 泛化化
template <typename... Args>
class MyClass {
  public:
    MyClass()
    {
        std::cout << "MyClass 泛化版本 this:" << this << std::endl;
    }
};

// 偏特化
template <typename First, typename... Others>
class MyClass<First, Others...> : private MyClass<Others...> {
  public:
    MyClass() : m_i(0)
    {
        std::cout << "MyClass() 特化版本 this:" << this << " " << typeid(m_i).name()
                  << " sizeof...(Others):" << sizeof...(Others) << std::endl;
    }

    MyClass(First first, Others... others) : m_i(first), MyClass<Others...>(others...)
    {
        std::cout << "MyClass(First first, Others... other) 特化版本 this:" << this << " " << typeid(m_i).name()
                  << " sizeof...(Others):" << sizeof...(Others) << std::endl;
    }

    First m_i;
};

int main(int argc, char** argv)
{
    // MyClass<>
    // MyClass<double>
    // MyClass<float, double>
    // MyClass<int, float, double>
    MyClass<int, float, double> myclass;
    MyClass<int, float, double> myclass1(10, 20.1, 30.2);
    return 0;
}

输出结果为:

MyClass 泛化版本 this:0x7ffe94716a90
MyClass() 特化版本 this:0x7ffe94716a90 d sizeof...(Others):0
MyClass() 特化版本 this:0x7ffe94716a90 f sizeof...(Others):1
MyClass() 特化版本 this:0x7ffe94716a90 i sizeof...(Others):2
MyClass 泛化版本 this:0x7ffe94716aa0
MyClass(First first, Others... other) 特化版本 this:0x7ffe94716aa0 d sizeof...(Others):0
MyClass(First first, Others... other) 特化版本 this:0x7ffe94716aa0 f sizeof...(Others):1
MyClass(First first, Others... other) 特化版本 this:0x7ffe94716aa0 i sizeof...(Others):2
非类型模板参数包的展开示例
#include <iostream>
#include <string>

// 泛化版本类模板
template <int... Args>
class MyClass {
  public:
    MyClass()
    {
        std::cout << "MyClass()   泛化版本 this=" << this << std::endl;
    }
};

// 偏特化版本
template <int First, int... Others>
class MyClass<First, Others...> : private MyClass<Others...> {
  public:
    MyClass()
    {
        std::cout << "MyClass() 偏特化版本 this=" << this << " First:" << First << " sizeof...(Others)"
                  << sizeof...(Others) << std::endl;
    }
};

int main(int argc, char** argv)
{
    MyClass<1, 2, 3, 4, 5> myclass;
    return 0;
}

输出结果:

MyClass()   泛化版本 this=0x7ffff2b6d0a7
MyClass() 偏特化版本 this=0x7ffff2b6d0a7 First:5 sizeof...(Others):0
MyClass() 偏特化版本 this=0x7ffff2b6d0a7 First:4 sizeof...(Others):1
MyClass() 偏特化版本 this=0x7ffff2b6d0a7 First:3 sizeof...(Others):2
MyClass() 偏特化版本 this=0x7ffff2b6d0a7 First:2 sizeof...(Others):3
MyClass() 偏特化版本 this=0x7ffff2b6d0a7 First:1 sizeof...(Others):4
模板模板参数包展开示例
#include <iostream>
#include <string>
#include <list>
#include <queue>
#include <vector>

// 泛化版本
template <typename T, template <typename> typename... Container>
class Base {
  public:
    Base()
    {
        std::cout << "Base::Base()   泛化版本 this=" << this << std::endl;
    }
};

// 偏特化版本
template <typename T, template <typename> typename FirstContainer, template <typename> typename... OtherContainers>
class Base<T, FirstContainer, OtherContainers...> : private Base<T, OtherContainers...> {
  public:
    Base()
    {
        std::cout << "Base() 偏特化版本 this=" << this << " sizeof...(OtherContainers)" << sizeof...(OtherContainers)
                  << std::endl;
        m_container.push_back(12);
    }

    FirstContainer<T> m_container;
};

template <typename T, template <typename> typename... Container>
class Derived : private Base<T, Container...> {
  public:
    Derived()
    {
        std::cout << "Derived::Derived() this=" << this << " typeid(T):" << typeid(T).name()
                  << " Container 参数个数:" << sizeof...(Container) << std::endl;
    }
};

int main(int argc, char** argv)
{
    Base<int, std::vector, std::list, std::deque> base;
    return 0;
}

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

#include <iostream>
#include <string>

// 泛化化
template <typename... Args>
class MyClass {
  public:
    MyClass()
    {
        std::cout << "MyClass 泛化版本 this:" << this << std::endl;
    }
};

// 特化
template <typename First, typename... Others>
class MyClass<First, Others...> {
  public:
    MyClass() : m_i(0)
    {
        std::cout << "MyClass() 特化版本 this:" << this << " " << typeid(m_i).name()
                  << " sizeof...(Others):" << sizeof...(Others) << std::endl;
    }

    MyClass(First first, Others... others) : m_i(first), m_o(others...)
    {
        std::cout << "MyClass(First first, Others... other) 特化版本 this:" << this << " " << typeid(m_i).name() << " "
                  << m_i << " sizeof...(Others):" << sizeof...(Others) << std::endl;
    }

    First              m_i;
    MyClass<Others...> m_o;
};

int main(int argc, char** argv)
{
    // MyClass<int, float, double>  --  MyClass<float, double> -- MyClass<double>  MyClass<>
    MyClass<int, float, double> myclass1(10, 20.1, 30.2);
    return 0;
}
通过tuple和递归调用展开参数包
#include <iostream>
#include <string>
#include <tuple>

// 泛化
// MyCount: 用于统计,从0开始
// MyMaxCount:表示参数数量可用sizeof...取得
template <int MyCount, int MyMaxCount, typename... T>
class MyClass {
  public:
    // 静态成员函数,借助tuple和get提取每个参数
    static void mys_func(const std::tuple<T...>& t)
    {
        std::cout << "value = " << std::get<MyCount>(t) << std::endl;  // 取出每个参数并输出
        MyClass<MyCount + 1, MyMaxCount, T...>::mys_func(t);  // 计数,每次+1,递归调用
    }
};

// 偏特化,结束递归调用
template <int MyMaxCount, typename... T>
class MyClass<MyMaxCount, MyMaxCount, T...> {
  public:
    static void mys_func(const std::tuple<T...>& t) {}
};

template <typename... T>
void my_func_tuple(const std::tuple<T...>& t)
{
    // 0 表示计数从0开始
    MyClass<0, sizeof...(T), T...>::mys_func(t);
}

int main(int argc, char** argv)
{
    std::tuple<float, int, int> mytuple(12.5f, 100, 200);

    my_func_tuple(mytuple);
    return 0;
}
基类参数包的展开
#include <iostream>
#include <string>
#include <tuple>

template <typename... TList>
class MyClass : public TList... {
  public:
    MyClass() : TList()...
    {
        std::cout << "MyClass this:" << this << std::endl;
    }
};

class PA1 {
  public:
    PA1()
    {
        std::cout << "PA1::PA1() this:" << this << std::endl;
    }

  private:
    char m_s1;
};

class PA2 {
  public:
    PA2()
    {
        std::cout << "PA2::PA2() this:" << this << std::endl;
    }

  private:
    char m_s1p[100];
};

class PA3 {
  public:
    PA3()
    {
        std::cout << "PA3::PA3() this:" << this << std::endl;
    }

  private:
    char m_s1p[200];
};

int main(int argc, char** argv)
{
    MyClass<PA1, PA2, PA3> obj;
    std::cout << "sizeof(obj):" << sizeof(obj) << std::endl;

    // PA1::PA1() this:0x7ffee91647a0
    // PA2::PA2() this:0x7ffee91647a1
    // PA3::PA3() this:0x7ffee9164805
    // MyClass this:0x7ffee91647a0
    // sizeof(obj):301

    return 0;
}

可变参模板的偏特化
#include <iostream>
#include <string>
#include <tuple>
// 泛化
template <typename... Args>
class MyClass {
  public:
    MyClass()
    {
        std::cout << "MyClass 泛化版 this:" << this << "  sizeof...(Args):" << sizeof...(Args) << std::endl;
    }
};

// 偏特化
template <typename First, typename... Others>
class MyClass<First, Others...> {
  public:
    MyClass()
    {
        std::cout << "MyClass<First, Others...> 偏特化 this:" << this << "  sizeof...(Others):" << sizeof...(Others)
                  << std::endl;
    }
};

// 偏特化
template <typename Arg>
class MyClass<Arg> {
  public:
    MyClass()
    {
        std::cout << "MyClass<Arg> 偏特化1 this:" << this << std::endl;
    }
};

// 偏特化
template <typename Arg1, typename Arg2>
class MyClass<Arg1, Arg2> {
  public:
    MyClass()
    {
        std::cout << " MyClass<Arg1, Arg2> 偏特化2 this:" << this << std::endl;
    }
};

int main(int argc, char** argv)
{
    MyClass<int>                obj1;
    MyClass<int, float>         obj2;
    MyClass<int, float, double> obj3;
    MyClass<>                   obj4;
    return 0;
}

模板的一些特殊继承关系

奇异递归模板模式(CRTP):Curiously Recurring Template Pattern

过将子类类型作为模板参数传给基类的一种模板的使用技巧

在基类中使用派生类对象
#include <iostream>
#include <string>

template <typename T>  // T代表派生类
class Base {  // Base是类模板
  public:
    void asDerived()
    {
        T& derived = static_cast<T&>(*this);// 派生类对象也是基类对象
        derived.func();  // 调用派生类的成员函数
    }
  
  private:
    Base() {}
    friend T;  // 派生类为友元
};

class Derived1 : public Base<Derived1>  // Derived1是一个普通类
{
  public:
    void func()
    {
        std::cout << "Derived1::func() run" << std::endl;
    }
};

template <typename T>
class Derived2 : public Base<Derived2<T>>  // Derived2是一个类模板
{
  public:
    void func()
    {
        std::cout << "Derived2::func() run" << std::endl;
    }
};

int main(int argc, char** argv)
{
    Derived1 d1;
    d1.asDerived(); //调用基类的成员函数

    return 0;
}
代码复用
#include <iostream>
#include <string>

template <typename T>
struct Shape
{
    // 在类模板中定义友元
    // 把派生类对象是否相等判断移到基类中实现,减少代码量
    friend bool operator==(const Shape<T>& obj1, const Shape<T>& obj2)
    {
        const T& tmp1 = static_cast<const T&>(obj1);
        const T& tmp2 = static_cast<const T&>(obj2);

        if (!(tmp1 < tmp2) && !(tmp2 < tmp1)) {
            return true;
        }

        return false;
    }
};

struct Square : public Shape<Square>
{
    Square(int len) : length(len) {}
    int length;
};

// 类外运算符重载
bool operator<(Square const& obj1, Square const& obj2)
{
    if (obj1.length < obj2.length) {
        return true;
    }
    return false;
}

int main(int argc, char** argv)
{
    Square obj1(12);
    Square obj2(10);
    if (obj1 == obj2) {
        std::cout << "obj1 == obj2" << std::endl;
    }
    else {
        std::cout << "obj1 != obj2" << std::endl;
    }

    return 0;
}
基类调用派生类接口与多态
#include <iostream>
#include <string>

template <typename T>
class Human {
  public:
    T& to_child()
    {
        return static_cast<T&>(*this);
    }

    void eat()
    {
        to_child().eat();
    }

  private:
    Human() {}
    friend T;
};

class Men : public Human<Men> {
  public:
    void eat()
    {
        std::cout << "Men like eat noodle" << std::endl;
    }
};

class Women : public Human<Women> {
  public:
    void eat()
    {
        std::cout << "Women like rice " << std::endl;
    }
};

template <typename T>
void test(Human<T>& obj)
{
    obj.eat();
}

int main(int argc, char** argv)
{
    Men   men;
    Women women;

    men.eat();
    women.eat();

    test(men);
    test(women);

    return 0;
}
实例化多套基类静态变量和方法
#include <iostream>
#include <string>

template <typename T>
class Base {
  public:
    static int getObjCnt()
    {
        return cnt;
    }

  protected:
    static int cnt;
};

template <typename T>
int Base<T>::cnt = 0;

class Derived1 : public Base<Derived1> {
  public:
    Derived1()
    {
        cnt++;
    }
};

class Derived2 : public Base<Derived2> {
  public:
    Derived2()
    {
        cnt++;
    }
};

int main(int argc, char** argv)
{
    Derived1 d11, d12, d13;
    Derived2 d21, d22;

    std::cout << "Derived1::getObjCnt() = " << Derived1::getObjCnt() << std::endl;
    std::cout << "Derived2::getObjCnt() = " << Derived2::getObjCnt() << std::endl;

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值