2016-12-06 45 views
2

我寫了我自己的「槽」又名「可調用包裝器」,因爲我想在其他對象上提供成員函數槽rebinding(即我需要一種方法來存儲成員函數指針和指向所討論的類的指針)。我的插槽類錯過std :: function有什麼功能?

我跑了一個小尺寸的測試,發現我的系統(64位Linux)上的std::function是我自己實現類似類的三倍(Clang/libC++)的兩倍(GCC/libstdC++),帶有大小爲16字節。對於非成員函數和lambda的實現是這樣(的const void*第一個參數是與這裏沒有顯示的成員函數插槽均勻性):

template<typename... ArgTypes> 
class slot 
{ 
public: 
    virtual ~slot() = default; 

    virtual void operator()(const void* object, ArgTypes...) const = 0; 

protected: 
    slot() = default; 
}; 

template<typename Callable, typename... ArgTypes> 
class callable_slot : public slot<ArgTypes...> 
{ 
public: 
    callable_slot(Callable function_pointer_or_lambda) : callable(function_pointer_or_lambda) {} 

    virtual void operator()(const void*, ArgTypes... args) const override { callable(args...); } 

private: 
    Callable callable; 
}; 

template<typename Callable> 
class callable_slot<Callable> : public slot<> 
{ 
public: 
    callable_slot(Callable function_pointer_or_lambda) : callable(function_pointer_or_lambda) {} 

    virtual void operator()(const void*) const override { callable(); } 

private: 
    Callable callable; 
}; 

template<typename Callable, typename... ArgTypes> 
using function_slot = callable_slot<Callable, ArgTypes...>; 

我理解一些target這裏沒有實現,但我不認爲任何缺失的函數都會增加對象的大小。

我在問的是:爲什麼std::function的尺寸比我上面的便宜實現大?

+3

'的std :: function'有一個小目標優化 – Brian

回答

0

您的課程功能與std::function提供的功能大不相同。您請求您的課程的用戶提供實際類型的「可調用」對象作爲模板的參數。

相反,std::function不需要這個,並能處理任何調用對象,只要它具有operator()與所需的接口。嘗試使用帶有未知類型對象的模板,例如std::bind的結果,您將知道我的意思。

由於功能差別很大,所以尺寸比較沒有實際意義。

+0

類型擦除可以通過make_slot輔助函數來處理,沒有? – rubenvb

1

function_slot需要Callable和設置的args...,並返回一個類型從slot<args...>繼承了virtual operator()

要將其作爲一個值多態使用,您必須將其包裝在智能指針中並將其存儲在堆中,並且必須將包裝類operator()轉發到slot<args...>之一。

std::function對應這個封裝,而不是你的slotcallable_slot對象。

template<class...Args> 
struct smart_slot { 
    template<class Callable> // add SFINAE tests here TODO! IMPORTANT! 
    smart_slot(Callable other): 
    my_slot(std::make_unique<callable_slot<Callable, Args...>>(std::move(other))) 
    {} 
    void operator()(Args...args) const { 
    return (*my_slot)(std::forward<Args>(args)...); 
    } 
    // etc 
private: 
    std::unique_ptr<slot<Args...>> my_slot; 
}; 

smart_slot比你的代碼更接近std::function。至於std::function而言,您所寫的所有內容都是std::function的用戶永遠不會看到的實現細節。

現在,這隻需要std::function是一個指針的大小。 std::function更大,因爲它具有所謂的小對象優化。

而不是隻存儲一個智能指針,它有自己的內存塊。如果傳入的對象適合該內存塊,則它會將該內存構建到位,而不是執行堆分配。

std::function基本上強制要做到這一點對於像傳遞函數指針那樣簡單的情況。質量實現爲更大和更復雜的對象執行。MSVC可以處理兩個尺寸爲std::string的對象。

這意味着,如果你這樣做:

std::function<void(std::ostream&)> hello_world = 
    [s = "hello world"s](std::ostream& os) 
    { 
    os << s; 
    }; 
hello_world(std::cout); 

它確實沒有動態分配