【问题标题】:What advantages does C++20's std::source_location have over the pre-defined macros __FILE__, __LINE__ and __FUNCTION__?C++20 的 std::source_location 与预定义的宏 __FILE__、__LINE__ 和 __FUNCTION__ 相比有什么优势?
【发布时间】:2026-01-30 04:55:01
【问题描述】:

在我目前的项目中,我使用过:

Log(__LINE__, __FUNCTION__, message);

但是新的 C++20 实用程序类std::source_location 带有函数line()column()file_name()function_name(),它们做同样的事情。因此,新的 C++20 方式将是:

log(std::string message,const std::source_location& location = std::source_location::current())
{
    std::cout << "Debug:"
              << location.file_name() << ':'
              << location.line() << ' '
              << message << '\n';
}

新的 C++20 方式与旧的标准双下划线宏 __LINE____FILE____func__ 相比有什么优势? p>

我正在尝试确定优势是否如此之大,以至于有理由修改我当前项目中的代码以使用新的 std::source_location 对象而不是宏。

【问题讨论】:

  • 宏可能很危险。它们存在于类型系统之外,并且很难调试。 C++20方式OTOH,在类型系统中退出,更容易调试。
  • 通过std::source_location的示例进行完整讨论

标签: c++ c++20


【解决方案1】:

在 C++20 之前,您必须在冗长(手动将 __LINE____FILE____func__ 传递给每个调用)和使用宏为您完成这两者之间做出选择。

std::source_location 给你一个很好的调用语法,没有宏。没有其他隐藏的优势。

【讨论】:

  • 你可以自己制作struct SourceLocation { int line; char const* file, func; };。但是现在它是标准的,所以很好。
  • @AyxanHaqverdili 您不能在标准 C++ 中模仿 std::source_location::current()。不是没有 GCC 的 __builtin_LINE() 或等效的。
  • @AyxanHaqverdili std::source_location 需要编译器支持才能工作。它不能用纯 C++ 实现。
  • @HolyBlackCat SourceLocation current = { __LINE__, __FILE__, __FUNC__ };。然后将此对象发送到日志记录例程。不是自动的,但是是的。
  • @Sneftel 我最初误解了答案。我认为关键是它是一种包装精美的类型。看起来重点是您不需要输入 __LINE__ 和其他详细的宏。
【解决方案2】:

你的例子是我能想到的最好的动力。在过去,__LINE___FILE__ 是使用宏进行调试日志的最佳借口。现在这不再成立了。

在过去,您基本上有两个 choiches。要么让调用者传递有关日志记录发生位置的信息:

log(__LINE__,__FILE__,"Hello World");

或使用宏来获得相同的输出

log("Hello World");
//^--- log must be a macro if we want this __LINE__ in the log

两者都非常不方便,因为要么用户必须在每次调用时传递必须相同的参数,要么我们需要使用具有所有已知缺点的宏。使用source_location,我们可以在不涉及任何宏的情况下获得log("Hello World");,但仍然包含调用的行和文件,因为在调用站点替换了默认参数。例如:

#include <source_location>
#include <string>
#include <iostream>

void log(std::string message,const std::source_location& location = std::source_location::current())
{
    std::cout << "Debug:"
              << location.file_name() << ':'
              << location.line() << ' '
              << message << '\n';
}


int main() {
    log("hello");
    log("world");
}

Output:

Debug:/app/example.cpp:15 hello
Debug:/app/example.cpp:16 world

【讨论】:

    【解决方案3】:

    std::source_location 的主要优点主要是方便,因为它表示具有单一、易于存储的结构的源位置。

    它还具有不基于预处理器魔术的优点,因此对于该语言而言,它不仅仅是整数文字,而是有效类型,从类型安全的角度来看,这要好得多。

    如果您对旧方法感到满意,则没有理由更改已经运行良好的代码。不过,这对于大多数新功能来说通常是正确的,不仅适用于 std::source_location。

    【讨论】:

    • 嗯,整数文字有一个有效的类型。并且__LINE____FILE__ 不是“预处理器魔术”,除非您将整个预处理器视为魔术。
    • @PeteBecker 预处理器 is 从根本上说是“神奇的”,因为它甚至在解析之前就发生了,并且可以从主构建过程中分离到不同的步骤中(即通过运行 @ 987654323@ 或 cc -E)。因此,像__LINE__ 这样的宏完全失去了它们的语义意义,因为编译器只看到原始值,而不是标识符。没有办法将整数文字与 __LINE__ 区分开来,它甚至可以在无效的上下文中使用,因为替换发生在文本级别。
    • 如果您认为将编译分离为单独的阶段的可能性显示出一些深刻的含义,请记住,C++ 标准将编译描述为九个单独的阶段,可以分别和顺序地实现。在过去,编译器被实现为三个或四个独立的可执行文件,由驱动程序按顺序运行。每个阶段将其结果写入中间文件,下一个阶段从前一个阶段生成的文件中获取输入。
    最近更新 更多