基于参数数量推导可变参数模板的format函数实现问题
问题描述
现有两个函数:
void string(const char *str) { std::cout << "this is string" << std::endl; } void number(const char *str, double f) { std::cout << "this is number" << std::endl; }
需要编写通用包装函数format,支持如下调用方式:
int main() { format("single arg"); format("format string", 1.0); format("single arg", "format string", 1.0); format("format string 1", 1.0, "just string arg", "format string 2", 2.0); return 0; }
核心要求:
- 参数以「字符串,数字」成对出现时调用
number(),否则调用string() - 必须从右往左解包参数
尝试的错误实现如下:
template<class T> void operation(T first) { string(first); } template<class T, class U = float> void operation(T first, U second) { number(first, second); } template<class ... ARGS> void format(ARGS ... args) { auto last = (args, ...); using last_type = typename decltype(last); if constexpr (std::is_arithmetic_v<last_type>) (..., operation(args, last)); else (..., operation(args)); }
该实现的问题:解包operation(args, last)时,会将所有args与last配对传入,导致出现两个浮点数作为参数的非法调用,完全不符合需求。
解决方案
可以通过递归模板函数实现从右往左的参数处理逻辑,无需依赖元组,代码简洁且符合编译期类型检查要求:
#include <type_traits> #include <iostream> // 原函数保持不变 void string(const char *str) { std::cout << "this is string" << std::endl; } void number(const char *str, double f) { std::cout << "this is number" << std::endl; } // 递归终止条件:无参数时直接返回 void format() {} // 处理单个字符串参数(最基础的单参数情况) template<typename T> std::enable_if_t<std::is_same_v<std::decay_t<T>, const char*>> format(T str) { string(str); } // 优先处理末尾的「字符串+数字」配对:先调用number,再递归处理剩余参数 template<typename T, typename U, typename... Rest> std::enable_if_t<std::is_same_v<std::decay_t<T>, const char*> && std::is_arithmetic_v<std::decay_t<U>>> format(T str, U num, Rest... rest) { number(str, static_cast<double>(num)); format(rest...); } // 处理单个字符串参数后面还有其他参数的情况:先递归处理后面的参数,再调用string template<typename T, typename... Rest> std::enable_if_t<std::is_same_v<std::decay_t<T>, const char*>> format(T str, Rest... rest) { format(rest...); string(str); } // 测试用例 int main() { format("single arg"); format("format string", 1.0); format("single arg", "format string", 1.0); format("format string 1", 1.0, "just string arg", "format string 2", 2.0); return 0; }
关键逻辑说明
- 利用
std::enable_if和类型萃取做编译期分支判断,确保只有合法的参数组合会触发对应的函数重载 - 递归调用时严格遵循从右往左的处理顺序:
- 若末尾是「字符串+数字」,先处理该配对(调用
number),再递归处理前面的剩余参数 - 若末尾是单个字符串,先递归处理前面的所有参数,最后处理当前字符串(调用
string)
- 若末尾是「字符串+数字」,先处理该配对(调用
- 整个实现仅依赖C++17及以上的模板特性,无需额外容器或库支持
内容的提问来源于stack exchange,提问作者NikitaRock
相关产品推荐
相关产品推荐

