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

如何实现按调用位置(文件名+行号)仅执行一次代码的C++函数

问题描述

需要实现一个可多处调用的C++函数,要求按调用位置(文件名+行号),函数中部分代码仅执行一次,其余代码可重复执行。现有代码用std::source_location获取调用位置,但单次执行的代码每次调用都会运行,无法实现首次执行、后续跳过的效果。要求优先采用编译时逻辑,避免std::unordered_map这类数据结构,同时不希望std::call_once影响重复执行的代码。

现有代码:

#include <iostream>
#include <source_location>

void Bar(const std::source_location& location = std::source_location::current())
{
  // only do this once per unique location
  std::cout << location.file_name()
            << " (" << location.line() << ")"
            << std::endl;

  // but do this repeatedly
  std::cout << "call multiple times" << std::endl;
}

void Foo()
{
  std::cout << "Called" << std::endl;
  Bar();
}

int main()
{
  Bar();
  Foo();
  Foo();
  return 0;
}

当前输出:

example.cpp (23)
call multiple times
Called
example.cpp (18)
call multiple times
Called
example.cpp (18)
call multiple times

期望输出:

example.cpp (23)
call multiple times
Called
example.cpp (18)
call multiple times
Called
call multiple times
解决方案

要实现每个调用位置仅执行一次指定代码,同时不影响重复执行部分,我们可以利用模板非类型参数+静态局部变量的特性,结合宏来传递编译时的文件名和行号,确保每个调用位置生成唯一的模板实例,从而拥有独立的执行标记。

实现代码

#include <iostream>

// 模板函数:每个调用位置会生成唯一实例,拥有独立的静态标记
template<const char* File, int Line>
void Bar()
{
    // 仅首次调用该位置时执行
    static bool is_first_call = true;
    if (is_first_call)
    {
        std::cout << File << " (" << Line << ")" << std::endl;
        is_first_call = false;
    }

    // 每次调用都会执行的部分
    std::cout << "call multiple times" << std::endl;
}

// 宏包装,自动传递当前文件和行号到模板参数
#define BAR() Bar<__FILE__, __LINE__>()

void Foo()
{
    std::cout << "Called" << std::endl;
    BAR();
}

int main()
{
    BAR();
    Foo();
    Foo();
    return 0;
}

原理说明

  • 模板参数const char* File和int Line由宏BAR()自动填入编译时常量__FILE__(当前文件名)和__LINE__(当前行号),因此每个调用BAR()的位置会生成唯一的Bar模板实例。
  • 每个模板实例内部的static bool is_first_call是独立的,仅在该实例首次被调用时初始化并执行单次代码块,后续调用直接跳过。
  • 重复执行的代码不受静态变量影响,每次调用都会正常运行,完全符合需求。

输出结果

运行上述代码,输出与期望完全一致:

example.cpp (23)
call multiple times
Called
example.cpp (18)
call multiple times
Called
call multiple times

若不想使用宏,C23计划支持std::source_location作为模板非类型参数,但目前主流编译器尚未完全支持。在C20环境下,宏+模板的方案是最简洁且符合编译时逻辑要求的实现方式。

内容的提问来源于stack exchange,提问作者D.G. Redd

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 19:06:06