使用FMT库编译期检查格式化参数及默认参数问题求助
解决自定义日志器的两个FMT与source_location问题
问题1:"call to immediate function is not a constant" 编译错误
FMT_STRING要求传入编译期常量字符串,而std::string_view是运行时类型,无法作为常量表达式传递给FMT_STRING。解决方法是将格式化字符串作为模板非类型参数传入,确保它是编译期常量。
问题2:可变模板与source_location默认参数无法结合
C++中,若可变参数模板参数位于有默认值的参数之前,编译器无法区分哪些参数属于可变参数、哪些是默认参数的补位,导致无法省略std::source_location::current()。解决方法是调整参数顺序,将std::source_location参数放在可变参数之前,并保留默认值。
修正后的完整代码
#include <fmt/format.h> #include <string> #include <source_location> class LoggerTask { public: // 调整参数顺序:将source_location放在可变参数前,带默认值 template <std::size_t N, typename... Args> static void log( const char(&format_str)[N], const std::source_location loc = std::source_location::current(), const Args&... args ); private: static void log( std::string&& formatted_message, const std::source_location loc ); }; template <std::size_t N, typename... Args> void LoggerTask::log( const char(&format_str)[N], const std::source_location loc, const Args&... args ) { // 用FMT_STRING包装编译期常量字符串 std::string formatted_message = fmt::format(FMT_STRING(format_str), args...); log(std::move(formatted_message), loc); } void LoggerTask::log( std::string&& formatted_message, const std::source_location loc ) { // 实际日志写入逻辑,示例:打印位置信息和日志内容 fmt::print("[{}:{}] {}\n", loc.file_name(), loc.line(), formatted_message); } int main() { fmt::print("The answer is {}.\n", 42); // 现在可以省略source_location参数 LoggerTask::log("foo {}", 1); // 也可以手动指定位置(比如在封装函数中调用时) LoggerTask::log("bar {}{}", std::source_location::current(), 2, 3); }
关键修改说明
编译期字符串处理:
- 将格式化字符串改为模板非类型参数
const char(&format_str)[N],确保它是编译期常量数组,满足FMT_STRING的常量表达式要求。 - 这样
FMT_STRING(format_str)可以在编译期完成格式检查,避免运行时错误。
- 将格式化字符串改为模板非类型参数
参数顺序调整:
- 将
std::source_location参数移至可变参数Args...之前,并保留默认值std::source_location::current()。 - 调用时,编译器可以明确推断可变参数的边界,允许省略
source_location参数,符合预期用法。
- 将
优化日志传递:
- 将私有
log函数的参数改为std::string&&,避免不必要的字符串拷贝,提升性能。
- 将私有
内容的提问来源于stack exchange,提问作者user8694474
相关产品推荐
相关产品推荐

