You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用type_traits判断const char*?实现myPrintf模板函数参数校验

问题2:实现myPrintf的类型格式匹配检查

结合你已经了解的std::is_integral_v和std::is_floating_point_v,再加上刚才提到的类型判断工具,我们可以构建一个能检查参数与格式符匹配的模板函数。下面是一个实用的实现思路:

实现思路

  1. 先写一个辅助的constexpr函数,用于判断单个参数类型是否匹配给定的格式符;
  2. 用递归模板函数处理可变参数,逐个检查参数与对应的格式符;
  3. 可选:加入编译期断言,在编译阶段就发现类型不匹配的问题。

示例代码

#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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 22:12:46