模板
可变参数模板
可变参数模板(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
的基类。