如何用type_traits判断const char*?实现myPrintf模板函数参数校验
问题2:实现myPrintf的类型格式匹配检查
结合你已经了解的std::is_integral_v和std::is_floating_point_v,再加上刚才提到的类型判断工具,我们可以构建一个能检查参数与格式符匹配的模板函数。下面是一个实用的实现思路:
实现思路
- 先写一个辅助的
constexpr函数,用于判断单个参数类型是否匹配给定的格式符; - 用递归模板函数处理可变参数,逐个检查参数与对应的格式符;
- 可选:加入编译期断言,在编译阶段就发现类型不匹配的问题。
示例代码
#include <type_traits> #include <cstdio> #include <stdexcept> #include <utility> // 辅助函数:编译期判断参数类型是否匹配格式符 template<typename T> constexpr bool matches_format(char fmt) { switch(fmt) { case 'd': // %d匹配整数类型(排除bool,因为bool的打印行为可能不符合预期) return std::is_integral_v<T> && !std::is_same_v<T, bool>; case 'f': // %f匹配所有浮点类型(float/double/long double) return std::is_floating_point_v<T>; case 's': // %s匹配可转换为const char*的类型(包括const char*、char*) return std::is_convertible_v<T, const char*>; default: return false; } } // 递归终止函数:没有剩余参数时打印剩余格式字符串 void myPrintf(const char* fmt) { std::printf(fmt); } // 主模板函数:处理单个参数+剩余参数 template<typename T, typename... Args> void myPrintf(const char* fmt, T&& arg, Args&&... args) { if (*fmt == '\0') { throw std::runtime_error("Too many arguments provided for format string"); } if (*fmt == '%') { ++fmt; // 检查当前参数与格式符是否匹配 if (!matches_format<T>(*fmt)) { throw std::runtime_error("Argument type does not match format specifier"); } // 根据格式符打印参数 switch(*fmt) { case 'd': std::printf("%d", static_cast<int>(std::forward<T>(arg))); break; case 'f': std::printf("%f", static_cast<double>(std::forward<T>(arg))); break; case 's': std::printf("%s", static_cast<const char*>(std::forward<T>(arg))); break; default: std::printf("%%%c", *fmt); break; } ++fmt; } else { // 打印普通字符 std::putchar(*fmt); ++fmt; } // 递归处理剩余参数 myPrintf(fmt, std::forward<Args>(args)...); } // 编译期检查版本(要求格式字符串是编译期常量) template<typename T, typename... Args> constexpr void myPrintf_constexpr(const char* fmt, T&& arg, Args&&... args) { if (*fmt == '%') { ++fmt; // 编译期断言,不匹配直接报错 static_assert(matches_format<T>(*fmt), "Argument type mismatch with format specifier"); // ... 打印逻辑和上面一致 ... ++fmt; } else { ++fmt; } myPrintf_constexpr(fmt, std::forward<Args>(args)...); } // 使用示例 int main() { myPrintf("Hello %s! You have %d apples and %f kg of oranges.\n", "Alice", 5, 3.14); // myPrintf("Error test: %s", 123); // 运行时抛出异常 // myPrintf_constexpr("Compile error: %s", 123); // 编译阶段直接报错 }
注意点
- 如果要支持
std::string,需要专门处理:在matches_format中加入std::is_same_v<T, std::string>,并在打印时调用.c_str(); - 编译期检查版本要求格式字符串是编译期常量(比如字符串字面量),否则
static_assert无法生效。
内容的提问来源于stack exchange,提问作者newstudent
相关产品推荐
相关产品推荐

