自lambdas进入C++以来,“回调”接口是否已过时

Are `Callback` interfaces obsolete since lambdas entered C++?

本文关键字:接口 是否 过时 回调 进入 lambdas C++ 以来      更新时间:2023-10-16

在我们的代码库中,我们有很多这样的类:

class DirectoryCallback {
public: 
    virtual ~DirectoryCallback(){};
    virtual void process(const std::string &path) = 0;
};

它们通常只有一个目的:包装函数调用。扩展这些接口很无聊。无聊并没有坏处,但我想知道:既然C++中有lambdas和std::function,这种模式有什么用?因为这正是他们所能做的…包装函数调用。

这个答案有点乱,但需要考虑一些事情。。。。

虚拟函数——用作回调——本质上更具限制性:

  • 你知道派生类必须指定一个实现(给定= 0),但在派生对象的生存期内它不会变化(在初始化程序列表中设置const std::function对象也可以做类似的事情,但不必这样),

  • 您知道回调从构造函数完成到销毁都是活动的

相比之下,掌握指定一组回调lambda的代码的人必须更仔细地研究它们的设置方式和时间,以及它们在对象的生命周期中是否会发生变化。Lambdas可能会从创建它们的函数中捕获更多的输入和上下文,这增加了潜在的复杂性。

设置std::function回调可能需要更明确的过程代码来设置std::function变量,除非这是在构造函数的初始化列表中专门完成的。虚拟函数override更具声明性,这是一些人更喜欢的。

使用虚拟函数的最派生的实现,而在最派生的构造函数中为std::function指定lambda可能需要通过层次结构将其显式传递给所有级别的基类构造函数。

虚拟函数的const性质直观地起作用,但通过将std::function成员设置为lambda来指定行为的派生类有机会获得对派生对象的非const访问权限,而不管基类设计建议这些回调应该需要什么。

编译器可以很好地理解虚拟调度,并且当对象的动态类型在编译时已知时,通常会通过潜在的内联函数来解析虚拟调度。认为std::function对象必须实现等效优化是一种延伸,其中lambda名义上是由运行时的派生函数设置的。。。。

虚拟函数被有效地分解为指向每个类表的每个对象指针,这将是使用std::functions手动编排的更大麻烦。每次回调使用std::function成员的简单方法内存效率较低。