模板
可变参数模板
可变参数模板(Variadic Templates)是C++11引入的一种强大特性,它允许模板接受可变数量的模板参数或函数参数。可变参数模板为编写通用性更强的代码提供了极大的灵活性,特别是在处理多种类型或不确定数量的参数时。
- 模板参数包
- 定义:
template<typename... Args>中的Args...表示一个模板参数包(template parameter pack),它可以容纳零个或多个类型参数。 - 展开:
Args...表示对参数包的展开,通常在函数定义或调用时使用。
- 定义:
- 函数参数包
- 定义:
void func(Args... args)中的args...是一个函数参数包(function parameter pack),表示可以接受零个或多个函数参数。 - 展开:
args...可以在函数体内展开,用于递归调用、初始化列表或其他操作。
- 定义:
template <typename... Arguments> returntype functionname(Arguments... args);
template <typename... Arguments> returntype functionname(Arguments&... args);
template <typename... Arguments> returntype functionname(Arguments&&... args);
template <typename... Arguments> returntype functionname(Arguments*... args);
template <typename... Arguments> returntype functionname(const Arguments&... args);
可变参数模板使用 sizeof...() 运算符
template<typename... Arguments>
void tfunc(const Arguments&... args)
{
constexpr auto numargs{ sizeof...(Arguments) };
X xobj[numargs]; // array of some previously defined type X
helper_func(xobj, args...);
}
展开参数包(Parameter Pack Expansion):
-
展开参数包的基础形式:
args...是一个参数包,你可以将其展开到各种上下文中,如函数调用、构造函数调用、表达式等。template<typename... Args> void function(Args... args) { // 这里需要展开 args... 参数包 } -
递归展开:每次递归调用
print,会处理一个参数并将剩余的参数继续传递下去,直到参数包为空。这时,调用的是不带参数的基函数print(),这就是递归的终止条件。void print() { std::cout << "End of recursion" << std::endl; } template<typename T, typename... Args> void print(T first, Args... rest) { std::cout << first << std::endl; print(rest...); // 递归展开参数包 } int main() { print(1, 2, 3, "Hello", 4.5); return 0; } -
使用初始化列表展开:C++11 中,也可以利用初始化列表
{}进行参数包展开,常用于执行一些表达式的副作用,如输出或计算。这里(std::cout << args << std::endl, 0)...是参数包展开的关键部分,它将args...中的每个参数依次应用到std::cout << args << std::endl表达式中。template<typename... Args> void print(Args... args) { (void)std::initializer_list<int>{(std::cout << args << std::endl, 0)...}; } int main() { print(1, 2, 3, "Hello", 4.5); return 0; } -
C++17 Fold 表达式:C++17 引入了 fold 表达式,这是展开参数包的最简洁方法。fold 表达式:
(std::cout << ... << args)会将参数包args...中的每个参数依次应用到<<操作符中。最终结果是将所有参数依次输出。template<typename... Args> void print(Args... args) { (std::cout << ... << args) << std::endl; } int main() { print(1, 2, 3, "Hello", 4.5); return 0; }
类型萃取
基本类型判断萃取
-
std::is_integral<T>:判断类型T是否为整数类型(如int、char等)。std::cout << std::is_integral<int>::value << std::endl; // 输出: 1 (true) std::cout << std::is_integral<float>::value << std::endl; // 输出: 0 (false) -
std::is_floating_point<T>:判断类型T是否为浮点数类型(如float、double等)。std::cout << std::is_floating_point<double>::value << std::endl; // 输出: 1 (true) -
std::is_pointer<T>:判断类型T是否为指针类型。std::cout << std::is_pointer<int*>::value << std::endl; // 输出: 1 (true) std::cout << std::is_pointer<int>::value << std::endl; // 输出: 0 (false)
类型修饰符萃取
std::add_const,std::remove_const:用于添加或移除类型的const限定符。std::add_volatile,std::remove_volatile:用于添加或移除类型的volatile限定符。std::remove_cv,std::add_cv:同时处理类型的const和volatile修饰符。它们提供了一种方便的方法,可以同时移除或添加这两个修饰符。std::remove_reference<T>:移除类型T的引用限定符(包括左值和右值引用)。std::add_lvalue_reference<T>:将左值引用限定符添加到类型T。std::add_rvalue_reference<T>:将右值引用限定符添加到类型T。
条件选择萃取
根据条件选择不同的类。
std::conditional<Condition, T1, T2>:根据Condition选择T1或T2作为类型。std::enable_if<Condition, T>:当Condition为true时,类型为T,否则此模板不参与重载。
类型转换萃取
用于在类型之间进行转换
std::make_signed<T>:将类型T转换为对应的有符号整数类型。std::make_unsigned<T>:将类型T转换为对应的无符号整数类型。
类型判断萃取
判断两个类型之间的关系。
std::is_same<T1, T2>:判断类型T1和T2是否相同。std::is_base_of<Base, Derived>:判断Base是否为Derived的基类。