如何在C++中遍历无模板的可变参数?
问题分析与解决
首先明确:C++中类型安全的可变参数(参数包)必须依赖模板,你写的void variadic(X d, X others...)里的others...是C风格可变参数,和C++11引入的参数包完全是两种机制,没法直接用范围for遍历。
一、正确的实现方式:模板参数包
如果想要类型安全且方便遍历的可变参数,必须将成员函数改为模板函数,利用C++的参数包特性:
#include <iostream> class X { private: int val; public: X(int v) : val(v) {} int value() const { return val; } }; class CoolClass { public: // 模板参数包版本 template<typename... Args> void variadic(X d, Args&&... others) { std::cout << "Main is " << d.value() << std::endl; std::cout << "Others are: " << std::endl; // 用初始化列表配合范围for遍历参数包 for(const auto& o : {std::forward<Args>(others)...}) { std::cout << o.value() << std::endl; } // C++17及以后可用折叠表达式,更简洁 // (std::cout << std::forward<Args>(others).value() << std::endl, ...); } }; int main() { CoolClass cc; cc.variadic(X(1), X(2), X(3), X(4)); return 0; }
说明:
- 模板参数包
Args&&... others可以接受任意数量、兼容类型的参数(这里要求参数能调用value()方法) {std::forward<Args>(others)...}会把参数包展开成一个初始化列表,这样就能用范围for直接遍历- 折叠表达式是C++17的特性,无需初始化列表,可直接逐个处理参数包元素,代码更简洁
二、原代码无法运行的原因
你写的X others...在非模板函数中是C风格可变参数,这套语法继承自C,本质是把后续参数打包成无法直接遍历的黑盒,只能通过<cstdarg>头文件的va_start、va_arg、va_end宏逐个取出参数,存在以下问题:
- 类型不安全:编译器无法校验后续参数是否都是
X类型 - 无法自动获取参数个数,必须手动指定结束标志
- 没有可迭代的序列/容器,自然没法用范围for遍历
三、如果非要用C风格可变参数(不推荐)
若因特殊需求必须使用C风格可变参数,实现方式如下(不建议在C++中使用,类型安全风险极高):
#include <iostream> #include <cstdarg> class X { private: int val; public: X(int v) : val(v) {} int value() const { return val; } }; class CoolClass { public: // C风格可变参数版本,需手动指定后续参数个数 void variadic(X d, int count, ...) { std::cout << "Main is " << d.value() << std::endl; std::cout << "Others are: " << std::endl; va_list args; va_start(args, count); for(int i = 0; i < count; ++i) { X o = va_arg(args, X); // 注意:X必须是POD类型,否则行为未定义 std::cout << o.value() << std::endl; } va_end(args); } }; int main() { CoolClass cc; cc.variadic(X(1), 3, X(2), X(3), X(4)); // 第二个参数指定后续参数数量 return 0; }
注意:
- 必须额外传递参数个数,否则无法确定要取出多少个参数
va_arg取出的参数类型必须和实际传递的完全一致,若X是非POD类型,这种用法属于未定义行为,极易引发问题
内容的提问来源于stack exchange,提问作者Injenye Lojik
相关产品推荐
相关产品推荐

