如何理解 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;
}
尽管 MemFunctionInst
、FunctorExInst
、FunctionEx
、LambdaEx
都是不同的类型,但它们都可以分配给同一个 std::function
变量,因为一种叫做 type erasure.
的技术
根据文档 (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;
}
尽管 MemFunctionInst
、FunctorExInst
、FunctionEx
、LambdaEx
都是不同的类型,但它们都可以分配给同一个 std::function
变量,因为一种叫做 type erasure.