请考虑以下代码:
#include <iostream>
#include <functional>
using namespace std;
int main() {
auto f = [](int a = 3) {cout << a << endl; };
f(2); // OK
f(); // OK
auto g = f;
g(2); // OK
g(); // OK
function<void(int)> h = f;
h(2); // OK
h(); // Error! How to make this work?
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我怎么能声明和?h一样?fg
Yak*_*ont 12
std::function有一个固定的签名.这是一个设计选择,而不是硬性要求.编写std::function支持多个签名的伪并不难:
template<class...Sigs>
struct functions;
template<>
struct functions<> {
functions()=default;
functions(functions const&)=default;
functions(functions&&)=default;
functions& operator=(functions const&)=default;
functions& operator=(functions&&)=default;
private:
struct never_t {private:never_t(){};};
public:
void operator()(never_t)const =delete;
template<class F,
std::enable_if_t<!std::is_same<std::decay_t<F>, functions>{}, int>* =nullptr
>
functions(F&&) {}
};
template<class S0, class...Sigs>
struct functions<S0, Sigs...>:
std::function<S0>,
functions<Sigs...>
{
functions()=default;
functions(functions const&)=default;
functions(functions&&)=default;
functions& operator=(functions const&)=default;
functions& operator=(functions&&)=default;
using std::function<S0>::operator();
using functions<Sigs...>::operator();
template<class F,
std::enable_if_t<!std::is_same<std::decay_t<F>, functions>{}, int>* =nullptr
>
functions(F&& f):
std::function<S0>(f),
functions<Sigs...>(std::forward<F>(f))
{}
};
Run Code Online (Sandbox Code Playgroud)
使用:
auto f = [](int a = 3) {std::cout << a << std::endl; };
functions<void(int), void()> fs = f;
fs();
fs(3);
Run Code Online (Sandbox Code Playgroud)
实例.
这将为每个重载创建一个单独的lambda副本.通过精心铸造甚至可以为不同的过载提供不同的lambda.
您可以编写一个不执行此操作的程序,但它基本上需要std::function使用更高级的内部状态重新实现.
上面的更高级版本将避免使用线性继承,因为这会导致O(n ^ 2)代码和O(n)递归模板深度的签名数量.平衡的二进制树继承将其降低到生成的O(n lg n)代码和O(lg n)深度.
工业强度版本将存储传递的lambda一次,使用小对象优化,具有手动伪vtable,其使用二进制继承策略来调度函数调用,并且在所述伪vtable中存储调度到函数指针.在每个类(不是每个实例)的基础上需要O(#个签名)*sizeof(函数指针)空间,并且使用大约每个实例的开销std::function.
但这对于SO帖子来说有点多,不是吗?