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

循环赋值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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 00:02:41