如何实现按调用位置(文件名+行号)仅执行一次代码的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
相关产品推荐
相关产品推荐

