【问题标题】:In what situation do I have to use a std::function?在什么情况下我必须使用 std::function?
【发布时间】:2020-08-30 22:55:57
【问题描述】:

我很难想象模板无法涵盖的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, 100> &data, 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 的真正用例。”尝试实现相同的功能,但是当您稍后存储和调用时 - “回调”。你会看到区别。

标签: c++ templates std std-function


【解决方案1】:

std::function 类型擦除可调用类型并使其可以同质处理。

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

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

【讨论】:

    【解决方案2】:

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

    class A : public Base {
    
    public:
      virtual void forEach(std::function<void(int&)> f);
    }
    

    【讨论】:

      【解决方案3】:

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

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

      输入类型擦除。

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

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

      【讨论】:

        猜你喜欢
        • 2020-08-19
        • 1970-01-01
        • 1970-01-01
        • 2014-10-28
        • 2021-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多