如何创建可将参数转发给fmt::format且保留类型安全的函数?
问题根因
你遇到的编译报错本质是因为fmt::format和std::format的格式字符串参数要求是编译期常量:这类库的格式串类型(fmt::format_string/std::format_string)的构造函数是consteval的,必须在编译期完成格式合法性校验,普通函数的入参不属于常量表达式范畴,所以直接转发会触发编译错误。
问题1:兼容编译期校验的转发函数实现
通用实现方案
你只需要在自定义函数的参数列表中显式声明格式串的类型为对应库的format_string即可,同时用std::type_identity_t把格式串的模板参数设为非推导语境,让参数包从后续实参推导,即可获得和直接调用fmt::format/std::format完全一致的编译期错误提示:
#include <fmt/core.h> #include <utility> #include <type_traits> // 适配fmt库的版本 template <typename... Args> auto my_print(fmt::format_string<std::type_identity_t<Args>...> fmt, Args&&... args) { return fmt::format(fmt, std::forward<Args>(args)...); } // 后续切换到std::format只需要修改类型即可 #if __cpp_lib_format >= 202106L template <typename... Args> auto my_print(std::format_string<std::type_identity_t<Args>...> fmt, Args&&... args) { return std::format(fmt, std::forward<Args>(args)...); } #endif
上述写法适用于所有要求编译期常量格式串的格式化库,调用点的格式错误会直接在编译期抛出,和原生调用的体验完全一致。
条件跳过格式化的优化方案
针对你提到的「不需要格式化时完全跳过步骤」的需求,上述写法已经满足要求:格式串的校验在编译期完成,运行时如果不需要生成字符串,直接跳过format调用即可,不会产生任何格式化的运行时开销,示例如下:
template <typename... Args> void conditional_print(bool need_output, fmt::format_string<std::type_identity_t<Args>...> fmt, Args&&... args) { if (!need_output) return; // 只有需要输出时才执行格式化 auto formatted_str = fmt::format(fmt, std::forward<Args>(args)...); // 后续输出逻辑 }
问题2:std::format参数类型的推断方法
虽然cppreference标注std::format的第一个参数类型是未指定,但C++标准明确定义了对应的类型别名:
- 对于
char类型的格式串,对应类型为std::format_string<Args...> - 对于
wchar_t等其他字符类型,对应类型为std::basic_format_string<CharT, Args...>
如果需要手动推导这类模板函数的参数类型,可以用decltype配合std::declval实现类型萃取,示例如下:
// 萃取std::format对应参数包的格式串类型 template <typename... Args> using StdFormatStrType = decltype(std::format<Args...>(std::declval<const char*>(), std::declval<Args>()...))::format_string_type;
日常使用直接用标准定义的std::format_string别名即可,不需要手动萃取。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

