2012-09-17 34 views
9

互聯網上的一些資源(特別是this one)表示std :: function使用小封閉優化,例如,如果封閉件大小比數據的一定量的下它不分配堆(以上鍊接指示16個字節用於GCC)g ++:使用閉包類型初始化的std :: function始終使用堆分配?

所以我去到g挖++頭

看起來像是否被施加這樣的優化決定通過在 「功能」 標題(克++ 4.6.3)

static void 
_M_init_functor(_Any_data& __functor, _Functor&& __f) 
{ _M_init_functor(__functor, std::move(__f), _Local_storage()); } 

的代碼塊和一些線向下:

static void 
_M_init_functor(_Any_data& __functor, _Functor&& __f, true_type) 
{ new (__functor._M_access()) _Functor(std::move(__f)); } 

static void 
_M_init_functor(_Any_data& __functor, _Functor&& __f, false_type) 
{ __functor._M_access<_Functor*>() = new _Functor(std::move(__f)); } 
    }; 

例如如果_Local_storage()是true_type,比投放新的叫,否則 - _Local_storage的常新

確定指標是如下因素:

typedef integral_constant<bool, __stored_locally> _Local_storage; 

和__stored_locally:

static const std::size_t _M_max_size = sizeof(_Nocopy_types); 
static const std::size_t _M_max_align = __alignof__(_Nocopy_types); 

static const bool __stored_locally = 
(__is_location_invariant<_Functor>::value 
&& sizeof(_Functor) <= _M_max_size 
&& __alignof__(_Functor) <= _M_max_align 
&& (_M_max_align % __alignof__(_Functor) == 0)); 

最後:__is_location_invariant:

template<typename _Tp> 
struct __is_location_invariant 
: integral_constant<bool, (is_pointer<_Tp>::value 
       || is_member_pointer<_Tp>::value)> 
{ }; 

所以。就我所知,閉包類型既不是指針也不是成員指針。爲了驗證我甚至寫了一個小測試程序:

#include <functional> 
#include <iostream> 

int main(int argc, char* argv[]) 
{ 
    std::cout << "max stored locally size: " << sizeof(std::_Nocopy_types) << ", align: " << __alignof__(std::_Nocopy_types) << std::endl; 

    auto lambda = [](){}; 

    typedef decltype(lambda) lambda_t; 

    std::cout << "lambda size: " << sizeof(lambda_t) << std::endl; 
    std::cout << "lambda align: " << __alignof__(lambda_t) << std::endl; 

    std::cout << "stored locally: " << ((std::__is_location_invariant<lambda_t>::value 
    && sizeof(lambda_t) <= std::_Function_base::_M_max_size 
    && __alignof__(lambda_t) <= std::_Function_base::_M_max_align 
    && (std::_Function_base::_M_max_align % __alignof__(lambda_t) == 0)) ? "true" : "false") << std::endl; 
} 

,輸出是:

max stored locally size: 16, align: 8 
lambda size: 1 
lambda align: 1 
stored locally: false 

所以,我的問題是:是intializing的std ::與lambda函數總是堆結果分配?或者我錯過了什麼?

+0

我這個程序確認你的發現:http://ideone.com/kzae6U您可以檢查鐺(HTTP:// melpon.org/wandbox/),相同的程序只爲內存分配非常大的捕獲... – PiotrNycz

回答

1

std :: function的分配是一個實現細節;然而,最後我檢查了12個字節是msvc的最大函子大小,gcc爲16,boost + msvc爲24。

+0

adzm:是的,這是在問題開始的鏈接中提到的。但是,我沒有看到g ++ –

2

我敢打賭,如果你加入這樣的:

std::cout << "std::__is_location_invariant: " << std::__is_location_invariant<lambda_t>::value << std::endl; 

,你會得到回:

std::__is_location_invariant: 0 

至少那是什麼ideone says

+0

實際上是這樣,是的,這非常符合我的測試。問題是:這是最終的? Dobbs博士是錯誤的,我們總是有堆分配? –

+0

@AlexI,它真的取決於編譯器。 – MSN

7

從GCC 4.8.1開始,libstdC++中的std :: function只針對指向函數和方法的指針進行優化。因此,無論函數的大小(包括lambda包含),從它初始化一個std ::函數都會觸發堆分配。不幸的是,也不支持自定義分配器。

Visual C++ 2012和LLVM libC++可以避免分配給任何足夠小的仿函數。

請注意,對於此優化踢你的函數應履行std :: is_nothrow_move_constructible。這是爲了支持noexcept std :: function :: swap()。幸運的是,如果所有捕獲的值都滿足,lambda就滿足了這個要求。

您可以編寫一個簡單的程序來檢查各種編譯器的行爲:

#include <functional> 
#include <iostream> 

// noexpect missing in MSVC11 
#ifdef _MSC_VER 
# define NOEXCEPT 
#else 
# define NOEXCEPT noexcept 
#endif 

struct A 
{ 
    A() { } 
    A(const A&) { } 
    A(A&& other) NOEXCEPT { std::cout << "A(A&&)\n"; } 

    void operator()() const { std::cout << "A()\n"; } 

    char data[FUNCTOR_SIZE]; 
}; 

int main() 
{ 
    std::function<void()> f((A())); 
    f(); 

    // prints "A(A&&)" if small functor optimization employed 
    auto f2 = std::move(f); 

    return 0; 
}