> 文章列表 > TGP 模板基础知识--04可变参数模板

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

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();  // 0func(1, "a", 3, 'b', 5, 6);  // 6return 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);  // 6func();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):301return 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;
}