std::function在使用时遇到的问题

1:为什么function是运行时的多态

要分清编译时和运行时,最简单的方法就是看决议(dispatch)发生在什么时候。

编译时多态(模板/重载):编译器在编译期就确定了要调用哪个函数,直接把函数地址写死在机器码里。

运行时多态(虚函数/std::function):编译器不知道具体调谁,需要在程序跑起来后,根据内存里的数据(虚表指针或函数指针)去跳转。

cpp 复制代码
// 外部调用
m_invoker->invoke(x); 

在这行代码里,m_invoker 的类型是 IInvoker*(基类指针)。编译器在编译这行代码时,根本不知道这个指针指向的是 InvokerImpl<Lambda> 还是 InvokerImpl<函数指针>。

程序运行到这一行,CPU 会读取 m_invoker 指向的内存地址,从该对象中取出虚函数表指针(vptr),再从虚表中找到 invoke 函数的实际地址,最后跳转过去。

这个"查表-跳转"的动作只能发生在程序运行期间,因为只有运行时,内存里才有了具体的对象实例。

所以,std::function 本质上就是通过虚函数机制(或等效的函数指针模拟)实现的运行时多态

2.:为什么 std::function 难以(甚至无法)被内联?

我加了 inline 关键字,或者编译器开了 O2,为什么还是内联不了?

核心原因一句话:内联(Inlining)是编译期的行为,而 std::function 的调用目标在编译期是未知的。

内联要求编译器在编译当前 .cpp 文件时,必须清清楚楚地看到被调用函数的函数体,并且确认调用的地址是确定的。

我们在代码里分析一下编译器的视角:

场景 A:直接调用 Lambda(可以内联)

cpp 复制代码
auto lambda = [](int x){ return x + 1; };
lambda(10); 

编译器看到 lambda(10),在编译期就知道要调用 Lambda::operator()。它直接把 return x + 1 这行代码插入到调用处,根本没有"函数调用"的机器码。零开销。

场景 B:通过 MyFunction 调用(难以内联)

cpp 复制代码
MyFunction f = lambda;
f(10); // 这里发生了什么?

编译器在编译 f(10) 这行时,只能看到 MyFunction::operator() 的定义:

cpp 复制代码
void operator()(int x) const {
    if (m_invoker) {
        m_invoker->invoke(x); // 这里是一个虚函数调用!
    }
}

编译器视角的分析:

它看到了 m_invoker->invoke(x),这是一个通过指针调用的虚函数。

编译器并不知道 m_invoker 指向的是谁。它可能指向 InvokerImpl<Lambda>,也可能指向 InvokerImpl<Functor>。

编译器不可能把"所有可能的子类函数体"都塞进这一个调用点,因为如果塞了 Lamba 的函数体,万一运行时传进来的是函数指针,程序就崩了。

因此,编译器只能放弃内联,生成一条标准的汇编指令:call rax + 8(从寄存器读取地址跳转)

在现代编译器中(如 GCC 的 -flto 链接时优化),有一种极其特殊的情况叫去虚拟化,想了解的可以下去了解一下

相关推荐
码林拾遗WJ13 小时前
告警分级的自动化实测:模型判断 vs 人工拍板
人工智能·算法·自动化
Tisfy13 小时前
LeetCode 0453.最小操作次数使数组元素相等:两次遍历(反向思维)
算法·leetcode·题解·遍历·反向思维
君万14 小时前
【LeetCode每日一题】回溯:46. 全排列 78. 子集 39. 组合总和
算法·leetcode·go
Keep_Trying_Go14 小时前
Consistent-Point半监督人群计数算法详解
人工智能·深度学习·算法·计算机视觉·人群计数
在所不辞兄14 小时前
【零基础学智能仿真-69】综合项目——材料参数驱动的仿真研究
人工智能·深度学习·神经网络·算法·机器学习·1024程序员节
凉茶钱15 小时前
红黑树:用“颜色”换“平衡”,set 与 map 的底层发动机
开发语言·c++·红黑树
怕浪猫16 小时前
Super-agent 在各大模型厂商的应用情况
算法·面试·github
海绵宝宝转agent16 小时前
LeetCode100 二叉树个人题解
算法·leetcode·深度优先
octopus_c16 小时前
C++ 模板详解:函数模板、类模板、特化与分离编译
开发语言·c++·算法
東隅已逝,桑榆非晚16 小时前
算法1. 双指针法-移动零
c++·算法