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

如何修复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);
}

关键改动说明

  1. 模板化存储格式串:将format_string_with_location改为模板类,用std::string_view模板参数存储格式化字符串,确保字符串在编译期保持常量性。
  2. 推导指南简化调用:添加类模板推导指南,让编译器自动推导模板参数,无需手动指定,保持调用方式的简洁性。
  3. 常量传递格式串:在debug函数中,直接使用模板参数FormatStr构造format_string,完全满足format_to对常量表达式格式串的要求。

内容的提问来源于stack exchange,提问作者Radim Kalaš

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 18:03:36