首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用闭包类型初始化的g++:std::函数总是使用堆分配吗?

用闭包类型初始化的g++:std::函数总是使用堆分配吗?
EN

Stack Overflow用户
提问于 2012-09-17 08:56:45
回答 3查看 2.9K关注 0票数 10

互联网上的一些消息来源(特别是this one)说,std::function使用小闭包优化,例如,如果闭包大小小于一定的数据量,它不会分配堆(上面的链接表示16个字节用于gcc)

所以我深入研究了g++头文件

看起来是否应用这种优化是由"functional“头中的这段代码决定的(g++ 4.6.3)

代码语言:javascript
复制
static void
_M_init_functor(_Any_data& __functor, _Functor&& __f)
{ _M_init_functor(__functor, std::move(__f), _Local_storage()); }

下面是几行代码:

代码语言:javascript
复制
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,否则调用常规new

_Local_storage的定义如下:

代码语言:javascript
复制
typedef integral_constant<bool, __stored_locally> _Local_storage;

和__stored_locally:

代码语言:javascript
复制
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:

代码语言:javascript
复制
template<typename _Tp>
struct __is_location_invariant
: integral_constant<bool, (is_pointer<_Tp>::value
               || is_member_pointer<_Tp>::value)>
{ };

所以。据我所知,闭包类型既不是指针也不是成员指针。为了验证我甚至写了一个小的测试程序:

代码语言:javascript
复制
#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;
}

输出结果为:

代码语言:javascript
复制
max stored locally size: 16, align: 8
lambda size: 1
lambda align: 1
stored locally: false

所以,我的问题是:用lambda初始化std::function总是会导致堆分配吗?还是我错过了什么?

EN

回答 3

Stack Overflow用户

发布于 2013-06-24 22:51:24

从GCC 4.8.1开始,libstdc++中的std::函数只针对指向函数和方法的指针进行优化。因此,无论您的functor的大小如何(包括lambda),从它初始化std::函数都会触发堆分配。不幸的是,也不支持自定义分配器。

Visual C++ 2012和LLVM libc++确实避免了分配给任何足够小的functor。

注意,对于这个优化来说,你的函数式函数应该满足std::is_nothrow_move_constructible。这是为了支持除std::function::swap()之外的任何其他函数。幸运的是,如果所有捕获的值都满足这个要求,那么lambda就会满足这个要求。

您可以编写一个简单的程序来检查各种编译器上的行为:

代码语言:javascript
复制
#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;
}
票数 7
EN

Stack Overflow用户

发布于 2012-09-17 09:31:08

我敢打赌,如果你加上这个:

代码语言:javascript
复制
std::cout << "std::__is_location_invariant: " << std::__is_location_invariant<lambda_t>::value << std::endl;

你会得到:

代码语言:javascript
复制
std::__is_location_invariant: 0

至少ideone says是这么想的。

票数 2
EN

Stack Overflow用户

发布于 2012-09-17 09:05:38

函数的分配是一个实现细节;但是我最后检查了一下,12字节是msvc的最大函数器大小,16字节是gcc的函数大小,24字节是boost+msvc的函数大小。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12452022

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档