C++实现间隔N次调用执行函数的静态变量冲突及返回值约束问题
问题解决方法
1. 核心问题分析
计数器共用问题
原有实现中计数器共用的根因是:C++模板函数的静态变量属于每个独立的模板实例,当多个调用的模板参数(N、Callable类型、Args类型)完全相同时,编译器只会生成一份函数实例,对应的静态计数器自然会被多个调用位点共享。
示例中两处CALL_EVERY_N(3, test, i, 1)和CALL_EVERY_N(3, test, i, 2)的模板参数完全一致,因此共用计数器。
返回值约束问题
需要利用C++类型萃取工具校验可调用对象传入指定参数后的返回值是否为void。
2. 解决方案代码(兼容C++17及以上)
修改后的实现
#include <cstddef> #include <functional> #include <utility> #include <type_traits> // 新增Line模板参数,用于区分不同调用位点 template<size_t N, size_t Line, typename Callable, typename... Args> void call_every_n(Callable&& c, Args&&... args) { static size_t call_count = 0; // 约束返回值必须为void static_assert(std::is_invocable_r_v<void, Callable, Args...>, "The input callable must return void when invoked with given arguments"); if(call_count == N - 1) { std::invoke(std::forward<Callable>(c), std::forward<Args>(args)...); call_count = 0; } else { ++call_count; } } // 宏中传入__LINE__作为Line模板参数,保证不同行的调用实例独立 #define CALL_EVERY_N(N, FUNC, ...) call_every_n<N, __LINE__, decltype(FUNC), decltype(__VA_ARGS__)>(FUNC, __VA_ARGS__)
测试运行结果
运行示例中的main函数测试用例,输出符合预期:
1 = 3 2 = 3 1 = 6 2 = 6 1 = 9 2 = 9 1 = 12 2 = 12 1 = 15 2 = 15 1 = 18 2 = 18
两个调用位点各自独立计数,每调用3次触发一次目标函数。
3. C++20优化版本(可选)
如果使用C++20及以上标准,可以用std::source_location替代__LINE__,支持更精准的位点区分(覆盖同一行多次调用、不同文件同行数调用的场景),同时可以用concept简化类型约束:
#include <source_location> template<size_t N, typename Callable, typename... Args> requires std::is_invocable_r_v<void, Callable, Args...> void call_every_n(Callable&& c, Args&&... args, const std::source_location& loc = std::source_location::current()) { struct CounterHolder { size_t count = 0; }; static thread_local CounterHolder counter; if(counter.count == N -1) { std::invoke(std::forward<Callable>(c), std::forward<Args>(args)...); counter.count = 0; } else { counter.count++; } } // 此时宏不需要额外传参 #define CALL_EVERY_N(N, FUNC, ...) call_every_n<N>(FUNC, __VA_ARGS__)
内容的提问来源于stack exchange,提问作者Jacopo Lottero
相关产品推荐
相关产品推荐

