> 文章列表 > C++可变参数函数模板和可变参数类模板

C++可变参数函数模板和可变参数类模板

C++可变参数函数模板和可变参数类模板

1. 可变参数函数模板

1.1 定义

可变参数函数模板指的是接受任意数量和类型的参数的函数模板,它具有语法上的可变参数,因此可以用于实现各种通用算法和容器。

定义可变参数函数模板需要使用模板参数包(template parameter pack)、函数参数包(function parameter pack)和参数包展开。

下面是可变参数函数模板的通用格式:

template <typename... Types>
RetType FuncName(Types... args);

其中,FuncName 是函数名,RetType 是函数返回值的类型。typename… Types 表示模板参数包,用于接受任意数量和类型的参数。Types… args 表示函数参数包,用于接受任意数量和类型的函数参数。

1.2 参数包展开

参数包展开是指将参数包中的每个参数都展开出来,以便进行相应操作。参数包展开可以使用递归、展开等技术来逐个处理每个参数,最终得到所需的结果。

函数模板的参数包通常需要使用递归函数 等实现。
更多展开方式

以下是可变参数函数模板的展开示例:

#include <iostream>// 递归终止条件
void print() {}// 递归展开
template <typename T, typename... Types>
void print(T var1, Types... var2) {std::cout << "var1 = " << var1 << std::endl;print(var2...);
}int main() {print(1, 2.5, "hello");return 0;
}

上述代码定义了一个可变参数函数模板 print,它递归展开参数包,并将每个参数打印输出。在递归展开的过程中,每次取出一个参数并打印,然后再递归调用函数本身,将剩余的参数包传入。

运行上述代码会输出以下结果:

var1 = 1
var1 = 2.5
var1 = hello

1.3 参数包类型推导

在函数模板中,可以使用 auto 关键字来推导参数包的类型,并且可以使用 decltype 关键字获取表达式的返回类型。

以下是可变参数函数模板的类型推导示例:

#include <iostream>template <typename... Types>
auto sum(Types... args) -> decltype((args + ...)) {return (args + ...);
}int main() {std::cout << "sum(1, 2, 3, 4, 5) = " << sum(1, 2, 3, 4, 5) << std::endl;std::cout << "sum(1.0, 2.5, 3.14) = " << sum(1.0, 2.5, 3.14) << std::endl;return 0;
}

上述代码定义了一个可变参数函数模板 sum,它递归展开参数包,并将每个参数累加。在返回值中使用了 decltype 来推导累加操作的返回类型。

运行上述代码会输出以下结果:

sum(1, 2, 3, 4, 5) = 15
sum(1.0, 2.5, 3.14) = 6.64

2. 可变参数类模板

2.1 定义

可变参数类模板指的是接受任意数量和类型的参数的类模板,它能够用于实现各种通用容器和算法。

定义可变参数类模板需要使用模板参数包、继承等技术。

下面是可变参数类模板的通用格式:

template <typename... Types>
class ClassName {
public:ClassName() {}private:// 成员变量和函数
};

其中,ClassName 是类名,typename… Types 表示模板参数包,用于接受任意数量和类型的参数。

2.2 参数包展开

参数包展开在类模板中可以作为类型参数、成员变量、成员函数等组成类的内容。类模板的参数包展开通常使用递归、继承等技术实现。

以下是可变参数类模板的展开示例:

#include <iostream>
#include <tuple>// 递归终止条件
template <typename... Types>
class Tuple {};// 类型展开
template <typename Head, typename... Tail>
class Tuple<Head, Tail...> : private Tuple<Tail...> {
public:using Base = Tuple<Tail...>;Tuple(Head head, Tail... tail) : m_head(head), Base(tail...) {}private:Head m_head;
};int main() {Tuple<int, double, std::string> t(1, 2.5, "hello");return 0;
}

上述代码定义了一个可变参数类模板 Tuple,它采用递归展开参数包的方式,以实现支持任意数量和类型的元组。

在递归展开的过程中,每次取出一个参数并作为当前类的成员变量,然后再继承下一个元素所形成的类。由于继承是从左到右依次展开的,因此需要使用 private 继承来确保每个子类只包含自己的成员变量。

2.3 参数包类型推导

在类模板中,可以使用模板参数推导来推导参数包的类型。模板参数推导是 C++17 新增的功能,可以使用 auto 关键字和类模板构造函数,省略模板参数列表中的类型参数,让编译器自动推导出参数包的类型。

以下是可变参数类模板的类型推导示例:

#include <iostream>
#include <tuple>template <typename... Types>
class Variant {
public:template <typename T>Variant(T value) : m_data(value) {}private:std::tuple<Types...> m_data;
};int main() {Variant v1(1);Variant v2(3.14);Variant v3("hello");return 0;
}

上述代码定义了一个可变参数类模板 Variant,它支持任意数量和类型的值类型,并采用了 std::tuple 来管理这些值。由于模板参数可以在模板构造函数中推导出,因此不需要显式指定类型参数。

运行上述代码会输出以下结果:

1
3.14
hello