This answer表示std::bind
按值返回一个对象,this comment表示分配给std::function
将导致堆分配存储{{1}返回的值}}。
有没有办法避免这种堆分配并直接通过值将std::bind
的返回值传递给另一个函数?
如果是这样,方法签名会将std::bind
替换为什么?
为了更明确,我有一个类似以下的功能。
std::function
假设有一个带有以下签名的函数void runThisFunction(std::function<void()> func);
。
foo
现在,我会按如下方式调用void foo(int a, int b);
。
runThisFunction
在此调用中,runThisFunction(std::bind(foo, 1, 2));
的输出转换为std::bind
,动态内存分配作为此过程的一部分发生。
是否可以将std::function
替换为可以直接通过值接收std::function
输出的其他声明,从而避免动态内存分配?
答案 0 :(得分:4)
是否可以将
std::function
替换为可以直接通过值接收std::bind
输出的其他声明,从而避免动态内存分配?
是的;但std::bind
返回的类型未指定,因此您需要使用模板来捕获类型;
template <typename F>
void runThisFunction(F func);
关于内存分配......
在此调用中,
std::bind
的输出转换为std::function
,动态内存分配作为此过程的一部分发生。
可以使用动态内存(但不是always),它取决于绑定到std::function
的仿函数的大小和实现的质量。
此外,C ++规范有§20.12.12.2.1/11;
[注意:鼓励实现避免为小型可调用对象使用动态分配的内存,例如,
f
是仅包含对象的指针或引用的对象以及成员函数指针。 - 结束说明]
即使有内存分配,我也不会太在意它。除非代码对性能至关重要,并且您已经对其进行了测量,否则所需的间接不应成为问题。
请记住,对于您的情况,foo
中绑定的bind
是指针,可能无论如何都不会有动态内存分配。
我开始研究这个问题,因为我通过仪器检测到意外的缓慢来测量转换上的缓存未命中
因此,您对性能有一些可测量的担忧......有使用std::bind
与std::function
配对的替代方法。 std::bind
是有用的通用粘合剂,但这并不意味着它也具有足够的性能 - 制作您自己的。自定义函子可以更高效。基于lambda的实现也很好看。不要忘记函数foo
也可以与std::function
一起使用,然后你完全放弃仿函数/活页夹(需要注意的是签名)。
在上面的引用中提到的“小对象”优化之前,对象需要“小”的一个附注似乎在库实现之间有点不同。
在coliru(libstdc ++)上,std::function
的参数大小需要为16个字节或更少,在MSVC上,限制为32个字节(这两个都是是32位平台)。使用clang ++(libc ++)64位编译时,此限制为24个字节......实际取决于实现需要在new
分配之前允许的空间。
我不确定性能有多重要,但也可以为目标计算此限制,然后应用优化,以使std::function
的参数保持在这些限制之下;例如使用指针或引用(也std::ref
)到struct
的参数(但必须注意这些不是悬空)。
答案 1 :(得分:1)
如果性能很重要但仍需要具体的接口,请考虑绑定到lambda而不是binder对象(通过std :: bind)。
我在gcc 5.3(libstdc ++, - 02)
上运行了这个测试#include <functional>
void foo(int, int);
void runThisFunction(std::function<void()> func);
void test()
{
runThisFunction(std::bind(&foo, 1, 2));
}
void test1()
{
runThisFunction([]{ foo(1, 2); });
}
test()
会调用new
。但是,std::function
中的小函数优化能够检测到test1()
中的lambda足够小,并且不会向代码中调用new
:
(注意:为清楚起见,删除了异常处理代码):
std::_Function_base::_Base_manager<test1()::{lambda()#1}>::_M_manager(std::_Any_data&, std::_Function_base::_Base_manager<test1()::{lambda()#1}> const&, std::_Manager_operation):
testl %edx, %edx
je .L3
cmpl $1, %edx
jne .L2
movq %rsi, (%rdi)
.L2:
xorl %eax, %eax
ret
.L3:
movq typeinfo for test1()::{lambda()#1}, (%rdi)
xorl %eax, %eax
ret
std::_Function_handler<void (), test1()::{lambda()#1}>::_M_invoke(std::_Any_data const&):
movl $2, %esi
movl $1, %edi
jmp foo(int, int)
test1():
subq $40, %rsp
movq %rsp, %rdi
movq std::_Function_handler<void (), test1()::{lambda()#1}>::_M_invoke(std::_Any_data const&), 24(%rsp)
movq std::_Function_base::_Base_manager<test1()::{lambda()#1}>::_M_manager(std::_Any_data&, std::_Function_base::_Base_manager<test1()::{lambda()#1}> const&, std::_Manager_operation), 16(%rsp)
call runThisFunction(std::function<void ()>)
movq 16(%rsp), %rax
testq %rax, %rax
je .L7
movl $3, %edx
movq %rsp, %rsi
movq %rsp, %rdi
call *%rax
.L7:
addq $40, %rsp
ret
typeinfo for test1()::{lambda()#1}:
typeinfo name for test1()::{lambda()#1}:
答案 2 :(得分:1)
通过使用引用包装器,您应该避免堆分配:
auto f = std::bind(foo, 1, 2);
runThisFunction(std::ref(f));
这是因为reference_wrapper
是一个小对象,并鼓励std::function
避免分配小(参见[func.wrap.func.con]):
注意:鼓励实施以避免动态使用 为小型可调用对象分配内存,例如f target是一个只持有a的对象 指针或对象的引用和成员函数指针。
仅当runThisFunction()
未在std::function
的生命周期后存储f
的值进行调用时,它才有效。
答案 3 :(得分:-1)
非常简单(至少对于gcc):
int someFunc(int a, int b) { return a+b; }
//....
int b = (std::bind(someFunc,3,4))(); // returns 7