C++用va_list实现接受任意数量argument对象的可变函数报错如何解决?
错误原因
- 第一个报错的直接原因:你给
madd_arguments的第一个形参加了const限定,但是你在for循环中会修改该参数的值(args = va_arg(vargs, const argument)),const修饰的对象不允许被修改,因此触发了丢弃限定符的报错。 - 第二个报错属于语法问题连锁触发的异常,此外你使用C风格可变参数
va_list处理非POD类型(argument类包含std::string成员,不属于POD类型)属于C++标准规定的未定义行为,即使修复了const问题,运行时也大概率出现崩溃、乱码等异常。
修复方案
临时修复C可变参数版本(不推荐)
仅做语法修复,不解决非POD参数的未定义行为问题:
- 修改
madd_arguments的形参定义,去掉const限定:
void madd_arguments(argument args, ...);
- 修改循环中
va_arg的目标类型为非const:
for (va_start(vargs, args); args.arg_name != "END_HERE"; args = va_arg(vargs, argument))
推荐方案:改用C++可变模板实现
可变模板是C++11及以上标准提供的类型安全的可变参数实现方案,完全兼容自定义类型,不需要哨兵参数和宏定义,代码如下:
完整实现代码
#include <vector> #include <string> #include <utility> // 用于完美转发 class argument{ private: std::string arg_name; public: argument(const std::string& ARG_NAME) : arg_name(ARG_NAME){} // 你代码中用到的set_flags可以在这里补充实现 // void set_flags(...) { ... } friend class parser; }; class parser { private: std::vector<argument> known_arguments; // 参数包展开终止函数 void add_arguments_impl() {} // 递归展开参数包 template<typename T, typename... Rest> void add_arguments_impl(T&& first, Rest&&... rest) { known_arguments.push_back(std::forward<T>(first)); add_arguments_impl(std::forward<Rest>(rest)...); } public: // 对外暴露的可变参数接口 template<typename... Args> void add_arguments(Args&&... args) { add_arguments_impl(std::forward<Args>(args)...); } }; // main函数不需要修改 int main() { argument a("name"); // a.set_flags("-v", "-verbose", "bobby", "jones"); argument b("string argument"); parser p; p.add_arguments(a,b); return 0; }
方案优势
- 编译期检查参数类型,类型安全,不会出现非POD参数的未定义行为
- 不需要额外定义宏和哨兵参数,代码可维护性更高
- 完美转发保证参数传递效率,左值、右值参数都能正确处理
内容的提问来源于stack exchange,提问作者JavaIsOkay1170
相关产品推荐
相关产品推荐

