如何修复C++日志函数中格式串非常量表达式的编译错误?
问题:带
std::source_location的日志函数编译失败 我尝试实现一个日志函数,自动通过std::source_location添加源码位置,同时将格式化字符串写入缓冲区。其中格式化函数format_to要求格式串为常量表达式,但调用封装后的debug函数时编译失败。
原始代码
#include <source_location> #include <string_view> #include <cstdint> struct format_string_with_location { consteval format_string_with_location(const char* s, const std::source_location& l = std::source_location::current()) : str(s), loc(l) {} std::string_view str; std::source_location loc; }; struct format_string { template<typename T> consteval format_string(const T& t) : str{t} {} std::string_view str; }; template<typename...Args> void format_to(format_string str, Args&&...) {} template<typename... Args> void debug(format_string_with_location format, const Args&... args) { format_to("{} {}:", format.loc.file_name(), format.loc.line()); format_to(format.str, args...); } int main() { debug("error: {}", 42); }
编译错误信息
<source>: In instantiation of 'void debug(format_string_with_location, const Args& ...) [with Args = {int}]': <source>:31:10: required from here <source>:27:14: in 'constexpr' expansion of 'format_string(format.format_string_with_location::str)' <source>:27:14: error: 'format' is not a constant expression 27 | format_to(format.str, args...); | ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
解决方案
问题核心:format_to要求传入的格式串是常量表达式,但debug函数中的format是运行时参数,其成员str无法被当作常量表达式传递给format_string的构造函数(format本身不是编译期常量)。
要保留格式化字符串的常量属性,需将其作为模板参数而非运行时成员存储,具体修复如下:
修复后的代码
#include <source_location> #include <string_view> #include <cstdint> // 用模板参数存储常量格式字符串 template<std::string_view FormatStr> struct format_string_with_location { consteval format_string_with_location(const std::source_location& l = std::source_location::current()) : loc(l) {} const std::source_location loc; }; // 辅助推导指南,简化调用时的模板参数推导 template<std::string_view S> format_string_with_location(const std::source_location& = std::source_location::current()) -> format_string_with_location<S>; struct format_string { template<std::string_view S> consteval format_string() : str{S} {} std::string_view str; }; template<typename...Args> void format_to(format_string str, Args&&...) {} // debug函数改为模板,接收带常量格式串的format_string_with_location实例 template<std::string_view FormatStr, typename... Args> void debug(format_string_with_location<FormatStr> format, const Args&... args) { format_to("{} {}:", format.loc.file_name(), format.loc.line()); // 直接用模板参数构造format_string,确保是常量表达式 format_to(format_string<FormatStr>{}, args...); } int main() { debug("error: {}", 42); }
关键改动说明
- 模板化存储格式串:将
format_string_with_location改为模板类,用std::string_view模板参数存储格式化字符串,确保字符串在编译期保持常量性。 - 推导指南简化调用:添加类模板推导指南,让编译器自动推导模板参数,无需手动指定,保持调用方式的简洁性。
- 常量传递格式串:在
debug函数中,直接使用模板参数FormatStr构造format_string,完全满足format_to对常量表达式格式串的要求。
内容的提问来源于stack exchange,提问作者Radim Kalaš
相关产品推荐
相关产品推荐

