循环赋值lambda至函数指针数组地址相同,如何获取不同实例?
循环中生成不同的无捕获Lambda函数指针实例
我需要给一个函数指针数组分配哑元函数,用无捕获Lambda实现时发现:逐个赋值能得到不同的指针地址,但循环赋值时所有地址都相同。
测试代码:
#include <iostream> int main() { int (*ptrArr[6])(void); int size = sizeof(ptrArr)/sizeof(void*); // 得到三个不同的指针地址 ptrArr[0] = [](){return 0;}; ptrArr[1] = [](){return 0;}; ptrArr[2] = [](){return 0;}; // 得到三个相同的指针地址 for(int i = 3; i < size; i++) { ptrArr[i] = [](){return 0;}; } for(int i = 0; i < 6; i++) { std::cout << (void*)ptrArr[i] << std::endl; } return 0; }
输出结果:
00E9F810 00E9F840 00E9F870 00E9F8A0 00E9F8A0 00E9F8A0
请问有没有办法在循环中获取不同的哑元函数实例?尝试过std::function但无法赋值给普通函数指针。
原因分析
无捕获Lambda可以隐式转换为普通函数指针,C++标准允许编译器将完全相同的无捕获Lambda合并为同一个函数实例。循环中重复定义的Lambda是完全相同的表达式,编译器会直接复用同一个函数地址;而逐个手动赋值的Lambda,部分编译器(如MSVC)会视为独立实例,但这不是标准强制要求的,换个编译器可能也会合并。
解决方案
要生成不同的函数指针实例,核心是让每个哑元函数成为独立的函数实体,最优雅的方式是利用模板函数的不同实例化:每个模板参数对应的实例都是独立函数,地址自然不同。
方法1:C++20 编译期循环(推荐)
利用std::index_sequence和折叠表达式实现编译期循环,生成不同的模板实例:
#include <iostream> #include <utility> // 模板哑元函数,不同模板参数对应不同实例 template<int N> int dummy_func() { return 0; } int main() { int (*ptrArr[6])(); constexpr int size = std::size(ptrArr); // 手动赋值部分 ptrArr[0] = [](){return 0;}; ptrArr[1] = [](){return 0;}; ptrArr[2] = [](){return 0;}; // 编译期循环生成不同模板实例 []<std::size_t... Is>(std::index_sequence<Is...>) { ((ptrArr[3 + Is] = dummy_func<3 + Is>), ...); }(std::make_index_sequence<3>()); for(int i = 0; i < size; ++i) { std::cout << (void*)ptrArr[i] << '\n'; } return 0; }
方法2:递归模板(兼容C++11/14)
如果不能用C++20,可以用递归模板实现编译期遍历:
#include <iostream> #include <type_traits> template<int N> int dummy_func() { return 0; } // 递归终止条件 template<int Start, int End> typename std::enable_if<Start >= End>::type assign_dummies(int (*[])()) {} // 递归赋值 template<int Start, int End> typename std::enable_if<Start < End>::type assign_dummies(int (*ptrArr[])()) { ptrArr[Start] = dummy_func<Start>; assign_dummies<Start + 1, End>(ptrArr); } int main() { int (*ptrArr[6])(); // 手动赋值前3个 ptrArr[0] = [](){return 0;}; ptrArr[1] = [](){return 0;}; ptrArr[2] = [](){return 0;}; // 递归赋值后3个 assign_dummies<3, 6>(ptrArr); for(int i = 0; i < 6; ++i) { std::cout << (void*)ptrArr[i] << '\n'; } return 0; }
注意事项
- 带捕获的Lambda无法隐式转换为普通函数指针,因此不能用捕获循环变量的方式区分实例(比如
[i](){return 0;}),这种Lambda只能用std::function包装,但std::function无法直接赋值给普通函数指针。 - 模板方法的核心是利用编译期常量生成不同的函数实例,确保每个函数指针指向独立的函数实体。
内容的提问来源于stack exchange,提问作者Jens
相关产品推荐
相关产品推荐

