C++17升级C++20后const char*不再是常量表达式的问题
C++20升级后spdlog日志封装编译错误分析与解决
问题背景
项目基于spdlog封装日志功能,在C17环境下编译运行正常。升级至C20后,调用vtek_log_error("Error, wrong parameter: {}", myParam);这类代码时触发编译错误,提示message不是常量表达式。
封装头文件代码
#pragma once #include <memory> #include <string> #include <spdlog/spdlog.h> namespace vtek { void initialize_logging(const InitInfo* info); void terminate_logging(); void disable_logging(); void flush_vtek_logger(); void flush_client_logger(); class LogContainer { public: static inline std::shared_ptr<spdlog::logger> sVtekLogger = nullptr; static inline std::shared_ptr<spdlog::logger> sClientLogger = nullptr; }; } template<typename... Args> inline void vtek_log_error(const char* message, const Args &... args) { vtek::LogContainer::sVtekLogger->error(message, args...); }
编译报错信息
in ‘constexpr’ expansion of ‘fmt::v9::basic_format_string<char, const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, const long unsigned int&>(message)’ /<path>/vtek_logging.h:97:47: error: ‘message’ is not a constant expression 97 | vtek::LogContainer::sVtekLogger->error(message, args...);
原因分析
问题根源在于spdlog依赖的fmt库对C++20的适配逻辑:
- C++20版本的fmt库(spdlog通常捆绑内置fmt)要求格式化字符串必须是编译期常量表达式,以此在编译阶段校验格式字符串与参数的匹配性,提升代码安全性与运行性能。
- 你传入的是字符串字面量,但
vtek_log_error函数中message是普通const char*参数——即便传入字面量,函数参数本身不属于常量表达式范畴,无法满足fmt库中basic_format_string的constexpr构造要求。
解决方案
修改封装函数的模板定义,将格式化字符串作为模板参数传入,让编译器将其识别为编译期常量:
#include <spdlog/fmt/ostr.h> // 确保引入必要的fmt头文件 template <fmt::format_string<Args...> Message, typename... Args> inline void vtek_log_error(Message&& message, Args&&... args) { vtek::LogContainer::sVtekLogger->error(std::forward<Message>(message), std::forward<Args>(args)...); }
也可利用spdlog提供的类型别名简化写法:
template <spdlog::format_string_t<Args...> Message, typename... Args> inline void vtek_log_error(Message&& message, Args&&... args) { vtek::LogContainer::sVtekLogger->error(std::forward<Message>(message), std::forward<Args>(args)...); }
修改后,传入字符串字面量时编译器会将其作为模板参数实例化,既满足fmt对常量表达式的要求,又保留了编译期格式检查的能力。
补充说明
这是spdlog/fmt库适配C20时的常见兼容性问题,并非编译器或代码逻辑的原生错误,核心是C20对constexpr能力的扩展,以及fmt库对编译期检查的强化。
内容的提问来源于stack exchange,提问作者alexpanter
相关产品推荐
相关产品推荐

