C++ std::function和std::bind是否进行动态内存分配?

C++ std::function和std::bind是否进行动态内存分配?,c++,C++,如果我这样做:- class Thing { ... void function (const std::string& message); }; std::list<std::function<void()>> work; 调用std::bind或使用std::function是否会导致使用new或其他方式进行动态内存分配?或者所有存储都是在编译时分配的?如果标准没有说明什么,那在visual studio 2012中呢,因为我的程序只需

如果我这样做:-

class Thing  
{
    ...
    void function (const std::string& message);
};

std::list<std::function<void()>> work;

调用std::bind或使用std::function是否会导致使用new或其他方式进行动态内存分配?或者所有存储都是在编译时分配的?如果标准没有说明什么,那在visual studio 2012中呢,因为我的程序只需要在此基础上构建,为了提高效率,我可能需要在考虑使用此机制的地方避免动态内存分配。

标准没有规定,但通常很容易看出,
std::function
至少在某些情况下必须分配内存:

struct huge { char c[10000]; };
void foo(const huge &);
std::function<void()>{std::bind(foo, huge{})};

我对此不确定。我想,正如ecatmur所建议的,这取决于该平台的std实现。对于类似的问题,我已经从代码项目中成功地使用了这个实现。它支持许多平台。有很好的文档记录,没有动态内存分配

应避免在游戏或模拟中运行时进行通用动态内存分配。问题并不总是碎片或大瓶颈(这两个都是可以避免的有效原因),而是时间量通常是不确定的。在这里,更特定于域的内存分配策略,如“池”或“帧”将是有利的


我只是针对g++的情况做了一些研究

谈到std::function和动态内存分配,有两个关键点

  • 函数可以存储任意大小的对象,这意味着在某些情况下它必须执行动态内存分配
  • 对于某些类型,std::function保证不会抛出异常。这意味着它必须在没有动态内存分配的情况下存储某些类型
  • gccs libstd+++中std::function的实现将在没有动态内存分配的情况下存储大小/对齐要求小于或等于其必须存储的对象的大小/对齐要求的其他对象

    在没有动态内存分配的情况下,它必须存储的最大内容是指向成员函数的指针。基于“ItAuthC++ C++ ABI”的编译器因此,您可以在g++中的std::函数中存储任何大小不超过两个指针的内容,而无需触发动态内存分配

    据我所知,std::bind只是将内容连接到一个对象中,因此将任何内容绑定到成员函数都将导致一个大小至少为三个指针的对象。将此对象分配给std::函数将导致动态内存分配

    更好的选择是使用lambda。这是静态地引用成员函数,为您提供最多捕获两个指针的空间,而不会触发动态内存分配

    为了演示,我在您的基础上编写了一些测试代码。我去掉了字符串和列表,使用了const char*(以避免std::string相关的内存分配)和placement new(此代码只用于构建,而不是运行),并将其输入godbolt

    #include <functional>
    using namespace std;
    
    class Thing  
    {
        void foo();
        void bar();
        void function (const char * message);
    };
    
    char baz[1024];
    
    void Thing::foo() {
        new (baz) std::function<void()>(std::bind(&Thing::function, this, "Hello"));
    }
    
    
    void Thing::bar() {
        const char * s = "Hello";
        new (baz) std::function<void()>([this,s](){function(s);});
    }
    
    #包括
    使用名称空间std;
    阶级事务
    {
    void foo();
    空心钢筋();
    无效函数(常量字符*消息);
    };
    char-baz[1024];
    void Thing::foo(){
    新的(baz)std::function(std::bind(&Thing::function,这个“Hello”);
    }
    void Thing::bar(){
    const char*s=“你好”;
    新的(baz)std::function([this,s](){function(s);});
    }
    
    结果令人满意

    Thing::foo():
            mov     r3, #0
            push    {r4, r5, r6, lr}
            ldr     r4, .L34
            mov     r6, r0
            sub     sp, sp, #16
            mov     r0, #16
            str     r3, [r4, #8]
            bl      operator new(unsigned int)
            ldr     r2, .L34+4
            mov     r1, #0
            mov     r3, r0
            str     r2, [sp]
            mov     r2, sp
            ldr     r5, .L34+8
            ldr     lr, .L34+12
            ldr     ip, .L34+16
            str     r1, [sp, #4]
            str     r6, [r0, #12]
            str     r0, [r4]
            str     r5, [r3, #8]
            ldm     r2, {r0, r1}
            str     lr, [r4, #12]
            stm     r3, {r0, r1}
            str     ip, [r4, #8]
            add     sp, sp, #16
            pop     {r4, r5, r6, pc}
            ldr     r3, [r4, #8]
            cmp     r3, #0
            beq     .L27
            ldr     r1, .L34
            mov     r2, #3
            mov     r0, r1
            blx     r3
    .L27:
            bl      __cxa_end_cleanup
    .L34:
            .word   .LANCHOR1
            .word   Thing::function(char const*)
            .word   .LC0
            .word   std::_Function_handler<void (), std::_Bind<void (Thing::*(Thing*,     char const*))(char const*)> >::_M_invoke(std::_Any_data const&)
            .word   std::_Function_base::_Base_manager<std::_Bind<void (Thing::*(Thing*, char const*))(char const*)> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
    Thing::bar():
                ldr     r2, .L38
                sub     sp, sp, #8
                stm     sp, {r0, r2}
                add     r2, sp, #8
                ldr     r3, .L38+4
                ldmdb   r2, {r0, r1}
                ldr     ip, .L38+8
                ldr     r2, .L38+12
                stm     r3, {r0, r1}
                str     ip, [r3, #12]
                str     r2, [r3, #8]
                add     sp, sp, #8
                bx      lr
        .L38:
                .word   .LC0
                .word   .LANCHOR1
                .word   std::_Function_handler<void (), Thing::bar()::{lambda()#1}>::_M_invoke(std::_Any_data const&)
                .word   std::_Function_base::_Base_manager<Thing::bar()::{lambda()#1}>::_M_manager(std::_Any_data&, std::_Function_base::_Base_manager<Thing::bar()::{lambda()#1}> const&, std::_Manager_operation)
    
    Thing::foo():
    mov r3#0
    推送{r4,r5,r6,lr}
    ldr r4、.L34
    mov r6,r0
    副警司,警司,#16
    mov r0,#16
    str r3[r4,#8]
    bl运算符新(无符号整数)
    ldr r2、.L34+4
    移动r1,#0
    mov r3,r0
    strr2[sp]
    莫夫r2,sp
    ldr r5、.L34+8
    ldr lr,.L34+12
    ldr ip,.L34+16
    str r1[sp,#4]
    str r6[r0,#12]
    strr0[r4]
    str r5[r3,#8]
    ldmr2,{r0,r1}
    str lr[r4,#12]
    stm r3,{r0,r1}
    str ip,[r4,#8]
    添加sp,sp,#16
    pop{r4,r5,r6,pc}
    ldr r3,[r4,#8]
    cmp r3,#0
    贝基L27
    ldr r1、.L34
    第2节第3节
    mov r0,r1
    blx r3
    .L27:
    bl\uuuu cxa\u end\u清理
    .L34:
    .单词.兰乔1
    .word Thing::函数(char const*)
    .word.LC0
    .word std::_函数_处理程序::_调用(std:_任意_数据常量&)
    .word std::_Function_base::_base_manager::_M_manager(std:_Any_data&,std:_Any_data const&,std:_manager_operation)
    Thing::bar():
    ldr r2,.L38
    副警司,警司,#8
    stm sp,{r0,r2}
    加上r2,sp,#8
    ldr r3、.L38+4
    ldmdb r2,{r0,r1}
    ldr ip,.L38+8
    ldr r2、.L38+12
    stm r3,{r0,r1}
    str ip,[r3,#12]
    str r2[r3,#8]
    添加sp、sp、#8
    bx-lr
    .L38:
    .word.LC0
    .单词.兰乔1
    .word std::_函数_处理程序::_调用(std:_任意_数据常量&)
    word std::_函数_base::_base _manager::_M_manager(std:_Any_data&,std:_函数_base:_base):_managerconst&,std:_manager(操作)
    
    我们可以清楚地看到在bind情况下有内存分配,但在lambda情况下没有


    *尽管名称由g++和clang++在许多不同的体系结构中使用。

    “为了效率,我可能”你不应该这样假设效率。使用
    std::list
    会在每次向列表中添加项目时导致内存分配。很好的“效率”。到目前为止,您所做的分析是否表明这段特定代码是性能瓶颈或内存占用?@kbok我不是在做假设,它是在收集数据库
    #include <functional>
    using namespace std;
    
    class Thing  
    {
        void foo();
        void bar();
        void function (const char * message);
    };
    
    char baz[1024];
    
    void Thing::foo() {
        new (baz) std::function<void()>(std::bind(&Thing::function, this, "Hello"));
    }
    
    
    void Thing::bar() {
        const char * s = "Hello";
        new (baz) std::function<void()>([this,s](){function(s);});
    }
    
    Thing::foo():
            mov     r3, #0
            push    {r4, r5, r6, lr}
            ldr     r4, .L34
            mov     r6, r0
            sub     sp, sp, #16
            mov     r0, #16
            str     r3, [r4, #8]
            bl      operator new(unsigned int)
            ldr     r2, .L34+4
            mov     r1, #0
            mov     r3, r0
            str     r2, [sp]
            mov     r2, sp
            ldr     r5, .L34+8
            ldr     lr, .L34+12
            ldr     ip, .L34+16
            str     r1, [sp, #4]
            str     r6, [r0, #12]
            str     r0, [r4]
            str     r5, [r3, #8]
            ldm     r2, {r0, r1}
            str     lr, [r4, #12]
            stm     r3, {r0, r1}
            str     ip, [r4, #8]
            add     sp, sp, #16
            pop     {r4, r5, r6, pc}
            ldr     r3, [r4, #8]
            cmp     r3, #0
            beq     .L27
            ldr     r1, .L34
            mov     r2, #3
            mov     r0, r1
            blx     r3
    .L27:
            bl      __cxa_end_cleanup
    .L34:
            .word   .LANCHOR1
            .word   Thing::function(char const*)
            .word   .LC0
            .word   std::_Function_handler<void (), std::_Bind<void (Thing::*(Thing*,     char const*))(char const*)> >::_M_invoke(std::_Any_data const&)
            .word   std::_Function_base::_Base_manager<std::_Bind<void (Thing::*(Thing*, char const*))(char const*)> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
    Thing::bar():
                ldr     r2, .L38
                sub     sp, sp, #8
                stm     sp, {r0, r2}
                add     r2, sp, #8
                ldr     r3, .L38+4
                ldmdb   r2, {r0, r1}
                ldr     ip, .L38+8
                ldr     r2, .L38+12
                stm     r3, {r0, r1}
                str     ip, [r3, #12]
                str     r2, [r3, #8]
                add     sp, sp, #8
                bx      lr
        .L38:
                .word   .LC0
                .word   .LANCHOR1
                .word   std::_Function_handler<void (), Thing::bar()::{lambda()#1}>::_M_invoke(std::_Any_data const&)
                .word   std::_Function_base::_Base_manager<Thing::bar()::{lambda()#1}>::_M_manager(std::_Any_data&, std::_Function_base::_Base_manager<Thing::bar()::{lambda()#1}> const&, std::_Manager_operation)