互联网上的一些消息来源(特别是this one)说std :: function使用小封闭优化,例如:如果闭包大小低于某些数据量,则不会分配堆(上面的链接指示gcc为16个字节)
所以我开始挖掘g ++标题
看看是否应用这种优化是由“功能”标题中的这段代码决定的(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,则调用placement-new,否则为 - regular new
_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
所以,我的问题如下:初始化std :: function with lambda总是会产生堆分配?或者我错过了什么?
答案 0 :(得分:7)
从GCC 4.8.1开始,libstdc ++中的std :: function仅针对函数和方法的指针进行优化。因此,无论您的仿函数(包括lambdas)的大小如何,从它初始化std :: function会触发堆分配。不幸的是,也不支持自定义分配器。
Visual C ++ 2012和LLVM libc ++确实避免为任何足够小的仿函数分配。
注意,对于这个优化来启动你的仿函数应该满足std :: is_nothrow_move_constructible。这是为了支持noexcept std :: function :: swap()。幸运的是,如果所有捕获的值都符合这个要求,lambdas就会满足这个要求。
您可以编写一个简单的程序来检查各种编译器的行为:
#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;
}
答案 1 :(得分:2)
我敢打赌,如果你加上这个:
std::cout << "std::__is_location_invariant: " << std::__is_location_invariant<lambda_t>::value << std::endl;
你会回来的:
std::__is_location_invariant: 0
至少那是ideone says。
答案 2 :(得分:1)
std :: function的分配是一个实现细节;但是最后我检查过,12个字节是msvc的最大函子大小,16个是gcc,24个是boost + msvc。