如何理解 std::function 的类型仅取决于其调用签名?

How to comprehend that the type of `std::function` depends solely on its call signature?

根据文档 (http://www.cplusplus.com/reference/functional/function/),它说:

Class that can wrap any kind of callable element (such as functions and function objects) into a copyable object, and whose type depends solely on its call signature (and not on the callable element type itself).

如何理解 std::function 的类型仅取决于其调用签名(而不取决于可调用元素类型本身)?有人可以通过一些简单的例子来说明吗?如果能对这个问题有所帮助,我将不胜感激。

这两个 "callable elements" 具有相同的调用签名(即 int(float)),尽管它们具有不同的类型:

struct Functor {
  int operator()(float arg) {
    ...
  }
};

int function(float arg) {
  ...
}

这意味着您可以使用相同的 std::function 类型来表示它们中的任何一个:

std::function<int(float)> f;
f = Functor();
f = function;

可调用对象有多种,即函数和函子。 std::function 被设计成不关心你给它的那些,例如,让我们考虑一个 std::function <void()>。这里我们说函数是 return nothing 和 takes nothing 的函数,这意味着

void foo() {}
auto bar = []{};
struct { void operator()(){} } baz;

是所有可以分配给它的东西,尽管它们都是不同的类型。

如果我们忘记所有血淋淋的细节,那么 std::function 的重要方法是 operator()

假设您为带有签名 double (int,int) 的可调用对象编写了一个包装器,那么该包装器将类似于:

struct example_wrapper {
     double operator()(int a,int b) { return 42; }
};

我遗漏的细节使您能够 std::function 包装任何类型的可调用对象。如果可调用对象具有相同的签名,那么包装器可以具有完全相同的 public 接口(即它是相同的类型)。如果可调用对象具有不同的签名,则包装器需要不同的 public 接口(即它是不同的类型)。

这意味着 std::function 中存储的可调用类型不会影响 std::function 本身的类型,只有函数签名会影响。

例如,这可以作为非优化实现:

template<class F, class Ret, class... Args>
Ret invoke_functor(void* func, Args&& ...args)
{
    return (*reinterpret_cast<F*>(f))(std::forward<Args>(args)...);
}

template<class Ret, class... Args>
Ret invoke_function(void* func, Args&& ...args)
{
    return reinterpret_cast<Ret(*)(Args...)>(func)(std::forward<Args>(args...);
}

template<class Ret, class... Args> //wrong but close enough
class function
{
public:
    template<class Functor>
    function(Functor&& f) :
        m_func(new Functor(f)), //leak
        m_invoke(&invoke_functor<Functor, Ret, Args...>)
    { }

    function(Ret(*ptr)(Args...)) :
         m_func(ptr),
         m_invoke(&invoke_function<Ret, Args...>)
    { }

     Ret
     operator()(const Args& ...args)
     {
          return m_invoke(m_func, args);
     }

private:
    void* m_func;
    Ret(*m_invoke)(void*, Args...);
}

此处 std::function 可以从函数和可调用结构创建,如其他答案所示。

模板类型有效地表示函数签名(当然不包括名称)。

std::function<bool(Bar const&, Foo const&)>

可以容纳仿函数、成员函数指针、函数指针或 lambda。但可调用对象必须具有 bool (Bar const&, Foo const&) 签名。

class Foo {};
class Bar {};
class FunctorEx
{
  public:
    bool operator()(Bar const&, Foo const&)
    {
        return true;
    }
} FunctorExInst;

class MemFunction
{
  public:
    bool MemFunctionEx(Bar const&, Foo const&)
    {
        return true;
    }
} MemFunctionInst;

bool FunctionEx(Bar const&, Foo const&)
{
    return true;
}

int main()
{
    auto LambdaEx = [] (Bar const&, Foo const&) -> bool
    {
        return true;
    };

    std::function<bool(Bar const&, Foo const&)> exFunctionVar;
    exFunctionVar = std::bind(&MemFunction::MemFunctionEx, &MemFunctionInst, std::placeholders::_1, std::placeholders::_2);
    exFunctionVar = FunctorExInst;
    exFunctionVar = FunctionEx;
    exFunctionVar = LambdaEx;
}

尽管 MemFunctionInstFunctorExInstFunctionExLambdaEx 都是不同的类型,但它们都可以分配给同一个 std::function 变量,因为一种叫做 type erasure.

的技术