如何理解 "std::function "的类型只取决于它的调用签名?

问题描述 投票:0回答:2

根据文档(http:/www.cplusplus.comreferencefunctionalfunction),它说

可以将任何一种可调用元素(如函数和函数对象)包装成一个可复制对象的类,其类型仅取决于其调用签名(而不是可调用元素类型本身)。

如何理解类型为 std::function 只取决于它的调用签名(而不是可调用元素类型本身)? 谁能举一些简单的例子来说明一下吗?我很希望能得到一些帮助。

c++ c++11 std-function
2个回答
3
投票

模板类型实际上表示的是函数签名(当然不包括名字)。

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

可以容纳一个functor、成员函数指针、函数指针或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;
}

尽管 MemFunctionInst, FunctorExInst, FunctionEx, LambdaEx 都是不同的类型,它们都可以被分配到相同的。std::function 变量,由于一种叫做 抹杀.


2
投票

有不同种类的可调用,即函数和漏斗。 std::function 被设计成不管你给它的是哪一种,例如,让我们考虑一个 std::function <void()>. 在这里,我们说这个函数是一个什么也不返回,什么也不接受的函数,这意味着

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

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


1
投票

这两个 "可调用的元素 "具有相同的调用签名(即。int(float)),尽管它们有不同的类型。

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

int function(float arg) {
  ...
}

这意味着你可以使用相同的 std::function 类型来代表其中之一。

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

1
投票

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

考虑到你为可调用的对象写了一个包装器,其签名是 double (int,int),那么包装器就会是类似的东西。

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

我省略的细节是让你能够做的 std::function 封装任何类型的可调用对象。如果这些可调用对象具有相同的签名,那么包装器可以具有完全相同的公共接口(即它是相同的类型)。如果这些可调用对象有不同的签名,那么包装器需要一个不同的公共接口(即它是一个不同的类型)。


1
投票

这意味着简单的说,存储在 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 可以从函数和可调用结构中创建,如其他答案所示。

© www.soinside.com 2019 - 2024. All rights reserved.