C++中是否存在可变参数函数的参数容器?能否实现该容器?
问题:可变参数函数模板与自定义ArgList容器的适配
假设我们定义了如下可变参数函数模板:
template <typename... Types> auto variadic(Types... arguments) /*...*/
是否存在一种C++容器(暂称ArgList),能够支持以下代码正常运行?
ArgList args; args.push_back(arg1); args.push_back(arg2); /*...*/ variadic(args);
若不存在这类容器,是否可以实现它?
回答
C++标准库里没有直接符合需求的容器。原因很简单:标准容器要么存储同类型元素(比如std::vector),要么是编译期固定类型和大小的元组(std::tuple),没法同时满足“动态添加任意类型元素”和“直接传递给可变参数模板展开成参数列表”这两个要求。
不过我们可以自己实现一个这样的ArgList,核心是用类型擦除来存储不同类型的元素,再通过一个辅助接口把元素逐一传递给可变参数函数。
实现方案
下面是一个可行的实现思路:
- 用抽象基类做类型擦除,定义一个用于调用目标函数的纯虚接口。
- 用模板派生类存储单个元素和下一个节点,递归实现参数传递逻辑。
ArgList类持有节点链的头部,提供push_back添加元素,以及apply方法触发参数展开调用。
完整代码
#include <memory> #include <utility> // 抽象基类:类型擦除的核心 struct ArgBase { virtual ~ArgBase() = default; template <typename... FuncArgs> virtual void call(auto&& func, FuncArgs&&... args) const = 0; }; // 模板派生类:存储单个元素+下一个节点 template <typename T> struct ArgNode : ArgBase { T value; std::unique_ptr<ArgBase> next; ArgNode(T val, std::unique_ptr<ArgBase> nxt) : value(std::move(val)), next(std::move(nxt)) {} template <typename... FuncArgs> void call(auto&& func, FuncArgs&&... args) const override { if (next) { // 递归传递当前参数,继续处理下一个节点 next->call(std::forward<decltype(func)>(func), std::forward<FuncArgs>(args)..., std::forward<T>(value)); } else { // 所有参数传递完毕,调用目标函数 std::forward<decltype(func)>(func)( std::forward<FuncArgs>(args)..., std::forward<T>(value)); } } }; class ArgList { private: std::unique_ptr<ArgBase> head; public: ArgList() = default; // 禁止拷贝,允许移动语义 ArgList(const ArgList&) = delete; ArgList& operator=(const ArgList&) = delete; ArgList(ArgList&&) = default; ArgList& operator=(ArgList&&) = default; // 添加任意类型的元素 template <typename T> void push_back(T&& val) { head = std::make_unique<ArgNode<T>>( std::forward<T>(val), std::move(head)); } // 把存储的元素展开传递给目标函数 template <typename Func> void apply(Func&& func) const { if (head) { head->call(std::forward<Func>(func)); } } };
使用示例
#include <iostream> // 目标可变参数函数 template <typename... Types> void variadic(Types... arguments) { (std::cout << ... << arguments) << '\n'; } int main() { ArgList args; args.push_back(123); args.push_back("hello"); args.push_back(3.14f); // 方式1:显式指定模板参数 args.apply(variadic<int, const char*, float>); // 方式2:用lambda自动推导参数类型 args.apply([](auto... args) { variadic(args...); }); return 0; }
注意点
- 没法直接写
variadic(args),因为可变参数模板需要的是展开的参数列表,不是单个容器对象。我们通过apply方法间接完成参数的展开传递。 - 如果不需要动态添加元素,
std::tuple结合std::apply就能实现类似功能,但std::tuple的大小和类型是编译期固定的,不符合动态push_back的需求。
内容的提问来源于stack exchange,提问作者sajmon
相关产品推荐
相关产品推荐

