在什么情况下我必须使用std :: function?

问题描述

我很难想象std::function的真实用例无法被模板覆盖。每当我考虑使用std::function时,我都会找到一种避免它的方法:

// implementation using std::function
void forEach(std::array<int,100> &data,const std::function<void(int&)> &f)
{
    for (size_t i = 0; i < 100; ++i) {
        f(data[i]);
    }
}

// implementation using any functor which takes an int&
template <typename Callable>
void forEach(std::array<int,const Callable &f)
     requires std::is_invocable_v<Callable,int&>
{
    for (size_t i = 0; i < 100; ++i) {
        f(data[i]);
    }
}

诚然,使用std::function的实现要短一些,但是由于类型擦除,它每次迭代都需要一个虚拟调用,并且编译器无法很好地对其进行优化。 (Live example

那么std::function的真正用例是什么,而不能使用模板呢?完全不需要std::function吗?

解决方法

std::function类型可擦除可调用类型,并可以对其进行同质处理。

例如,您可以有一个回调向量:

std::vector<std::function<int(std::string)>> callbacks;
,

以类中的虚拟方法为例。带有模板参数的虚拟方法是不允许的

class A : public Base {

public:
  virtual void forEach(std::function<void(int&)> f);
}
,

function提供的是类型擦除。在模板不合适甚至不可行的地方,类型擦除非常有用。类型擦除的典型用例是代码中有两个地方,即A和B。A需要向B发送消息。但是这种发送需要通过中间代码C进行。

C不能作为模板,因为C是用于存储或传递某些数据的通用介质。考虑一个信号和时隙系统。插槽的特定用法要求特定的可调用签名,这是信号发送器发送的。现在,根据您提供的呼叫对象的类型,创建插槽可以作为模板。但是,那么您就无法在单个插槽中具有多个回调,因为您需要能够存储它们的数组以按顺序调用。您不能存储对象的异构容器。因此,插槽需要以一种可以调用它的方式存储可调用对象,而不必绑定到特定的可调用对象类型。签名是指定的,不是实际可调用的。

输入擦除类型。

std::function和类似的类型擦除类型适用于您的界面和内部实现 由于某种原因而不能作为模板的情况。可能是因为您需要存储具有共享接口的可能不同类型的对象的容器。这可能是因为中间代码建立在实际上无法处理模板的C样式的接口上。或者,也许您不想将包含泛型可调用对象的类型设置为模板,以便用户可以提供任意可调用对象(模板不是免费的,尤其是在编译时)。或其他各种因素。

但是一般的共同点是对象提供者和对象用户之间的中间代码不能为模板。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...