template<class...Sigs>
strucct functions:std::function<Sigs>...{
using std::function<Sigs>::operator()...;
template<class T,
std::enable_if<!std::is_same<std::decay_t<T>,fundtions>{}>,int> =0
>
functions(T&&t):
std::function<Sigs>(t)...
{}
};
上面是一個粗對象凸輪存儲多於一個operator()
的C++ 17草圖。
一個效率更高的人只會存儲一次對象,但存儲如何以多種方式調用它。我跳過了很多細節。
它不是真的std::function
,而是一個兼容的類型; std函數只存儲一種方法來調用對象。
這是一個「功能視圖」,可以使用任意數量的簽名。它不擁有待被稱爲的對象。
template<class Sig>
struct pinvoke_t;
template<class R, class...Args>
struct pinvoke_t<R(Args...)> {
R(*pf)(void*, Args&&...) = 0;
R invoke(void* p, Args...args)const{
return pf(p, std::forward<Args>(args)...);
}
template<class F, std::enable_if_t<!std::is_same<pinvoke_t, std::decay_t<F>>{}, int> =0>
pinvoke_t(F& f):
pf(+[](void* pf, Args&&...args)->R{
return (*static_cast<F*>(pf))(std::forward<Args>(args)...);
})
{}
pinvoke_t(pinvoke_t const&)=default;
pinvoke_t& operator=(pinvoke_t const&)=default;
pinvoke_t()=default;
};
template<class...Sigs>
struct invoke_view:pinvoke_t<Sigs>...
{
void* pv = 0;
explicit operator bool()const{ return pv; }
using pinvoke_t<Sigs>::invoke...;
template<class F, std::enable_if_t<!std::is_same<invoke_view, std::decay_t<F>>{}, int> =0>
invoke_view(F&& f):
pinvoke_t<Sigs>(f)...
{}
invoke_view()=default;
invoke_view(invoke_view const&)=default;
invoke_view& operator=(invoke_view const&)=default;
template<class...Args>
decltype(auto) operator()(Args&&...args)const{
return invoke(pv, std::forward<Args>(args)...);
}
};
Live example。
我使用C++ 17 using ...
,因爲C++ 14中的二叉樹實現是醜陋的。
爲您的使用情況下,它會像looke:
auto func_object = [](int i = 0){};
invoke_view<void(), void(int)> f1 = func_object;
std::function<void(int)> f3 = f1; // works
std::function<void()> f4 = f1; // works
注意,在invoke_view
缺乏生命週期管理的意思是當func_object
繼續存在以上纔有效。 (如果我們爲invoke視圖創建一個invoke視圖,那麼「inner」invoke視圖也由指針存儲,所以必須繼續存在;如果我們將invoke視圖存儲在一個std函數中,情況不會如此)。
目標的終身管理,做對了,需要一些工作。你會想要使用一個小的緩衝區優化和一個可選的智能指針或某些東西來獲得小lambda表達式的合理性能,並避免堆分配的開銷。
一個簡單樸素的始終堆分配解決方案將取代void*
與unique_ptr<void, void(*)(void*)>
和存儲{ new T(t), [](void* ptr){static_cast<T*>(ptr)->~T();} }
它(或類似)。
該解決方案使功能對象僅移動;使其可複製也需要鍵入擦除克隆操作。
你打算如何調用f1或f2(使用默認值)? – nakiya
@nakiya對於信號和插槽的實現,在連接時需要複製插槽(函數),信號使用從信號的調用位置傳遞的參數來調用插槽。如果允許具有默認參數的插槽連接到具有可以調用它的簽名的任何信號,那將會很不錯。 –