是否可以直接接受 std::bind 的输出作为值,而不转换为 std::function?
Is it possible to accept the output of `std::bind` directly as a value, without conversion to std::function?
This answer states that std::bind
returns an object by value, and this comment 意味着分配给 std::function
将导致堆分配存储由 std::bind
编辑的值 return。
有没有办法避免这种堆分配,将std::bind
的return值直接按值传递给另一个函数?
如果是这样,方法签名将用什么替换 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
替换为其他声明,这些声明将直接按值接收 std::bind
的输出,从而避免动态内存分配?
Is it possible to replace std::function
with some other declaration that would would receive the output of std::bind
directly by value, thereby avoiding the dynamic memory allocation?
是的,是的;但是std::bind
返回的类型是未指定的,所以你需要使用模板来捕获类型;
template <typename F>
void runThisFunction(F func);
关于内存分配...
In this invocation, the output of std::bind
is converted into an std::function
, and dynamic memory allocation occurs as part of this process.
可以使用动态内存(但不能使用 always),这取决于绑定到 std::function
中的仿函数的大小和实现的质量。
此外,C++ 规范有这个 §20.12.12.2.1/11;
[Note: Implementations are encouraged to avoid the use of dynamically allocated memory for small callable objects, for example, where f
is an object holding only a pointer or reference to an object and a member function pointer.
— end note ]
即使有,我也不会太在意内存分配。除非代码对性能至关重要并且您已经对其进行了测量,否则所需的间接访问应该不是问题。
请记住,对于您的情况,绑定在 bind
中的 foo
是一个指针,很可能无论如何都不会进行动态内存分配。
I started looking at this because I measured cache misses on the conversion due to unexpected slowness detected through instrumentation
所以您对性能有一些衡量的担忧...有替代方法可以将 std::bind
与 std::function
配对使用。 std::bind
是有用的通用活页夹,但这并不意味着它的性能也足够 - 制作您自己的。自定义仿函数可能性能更高。基于 lambda 的实现也很好看。不要忘记函数 foo
也可以与 std::function
一起使用,然后您完全放弃 functor/binder(注意,签名需要匹配)。
关于 "small" 对象需要如何 在上面引用中提到的 "small object" 优化之前的旁注似乎在库实现有点安静。
这里 coliru (libstdc++), the size of the argument for std::function
needs to be 16 bytes or less, on MSVC,限制是 32 字节(这两个看起来都是 32 位平台)。使用 clang++ (libc++) 64 位编译,这个限制是 24 字节......这真的取决于实现,在需要进行 new
分配之前,它们将允许多少 space。
我不确定性能有多重要,但也可以为您的目标计算此限制,然后应用优化,使 std::function
的参数保持在这些限制以下;例如使用指向 struct
的指针或引用(也 std::ref
)作为参数(但必须注意不要悬空)。
如果性能很重要,但您仍然想要一个具体的接口,请考虑绑定到 lambda 而不是活页夹对象(通过 std::bind)。
我 运行 这个测试是在 gcc 5.3 (libstdc++, -O2)
#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 , %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 , %esi
movl , %edi
jmp foo(int, int)
test1():
subq , %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 , %edx
movq %rsp, %rsi
movq %rsp, %rdi
call *%rax
.L7:
addq , %rsp
ret
typeinfo for test1()::{lambda()#1}:
typeinfo name for test1()::{lambda()#1}:
通过使用引用包装器,您应该避免堆分配:
auto f = std::bind(foo, 1, 2);
runThisFunction(std::ref(f));
这是因为 reference_wrapper
是一个小对象,并且鼓励 std::function
避免为小对象分配(参见 [func.wrap.func.con]):
Note: Implementations are encouraged to avoid the use of dynamically
allocated memory for small callable objects, for example, where f’s
target is an object holding only a
pointer or reference to an object and a member function pointer.
仅当 runThisFunction()
未存储 std::function
的值以便在 f
的生命周期后调用时才有效。
非常简单(至少对于 gcc 而言):
int someFunc(int a, int b) { return a+b; }
//....
int b = (std::bind(someFunc,3,4))(); // returns 7
This answer states that std::bind
returns an object by value, and this comment 意味着分配给 std::function
将导致堆分配存储由 std::bind
编辑的值 return。
有没有办法避免这种堆分配,将std::bind
的return值直接按值传递给另一个函数?
如果是这样,方法签名将用什么替换 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
替换为其他声明,这些声明将直接按值接收 std::bind
的输出,从而避免动态内存分配?
Is it possible to replace
std::function
with some other declaration that would would receive the output ofstd::bind
directly by value, thereby avoiding the dynamic memory allocation?
是的,是的;但是std::bind
返回的类型是未指定的,所以你需要使用模板来捕获类型;
template <typename F>
void runThisFunction(F func);
关于内存分配...
In this invocation, the output of
std::bind
is converted into anstd::function
, and dynamic memory allocation occurs as part of this process.
可以使用动态内存(但不能使用 always),这取决于绑定到 std::function
中的仿函数的大小和实现的质量。
此外,C++ 规范有这个 §20.12.12.2.1/11;
[Note: Implementations are encouraged to avoid the use of dynamically allocated memory for small callable objects, for example, where
f
is an object holding only a pointer or reference to an object and a member function pointer. — end note ]
即使有,我也不会太在意内存分配。除非代码对性能至关重要并且您已经对其进行了测量,否则所需的间接访问应该不是问题。
请记住,对于您的情况,绑定在 bind
中的 foo
是一个指针,很可能无论如何都不会进行动态内存分配。
I started looking at this because I measured cache misses on the conversion due to unexpected slowness detected through instrumentation
所以您对性能有一些衡量的担忧...有替代方法可以将 std::bind
与 std::function
配对使用。 std::bind
是有用的通用活页夹,但这并不意味着它的性能也足够 - 制作您自己的。自定义仿函数可能性能更高。基于 lambda 的实现也很好看。不要忘记函数 foo
也可以与 std::function
一起使用,然后您完全放弃 functor/binder(注意,签名需要匹配)。
关于 "small" 对象需要如何 在上面引用中提到的 "small object" 优化之前的旁注似乎在库实现有点安静。
这里 coliru (libstdc++), the size of the argument for std::function
needs to be 16 bytes or less, on MSVC,限制是 32 字节(这两个看起来都是 32 位平台)。使用 clang++ (libc++) 64 位编译,这个限制是 24 字节......这真的取决于实现,在需要进行 new
分配之前,它们将允许多少 space。
我不确定性能有多重要,但也可以为您的目标计算此限制,然后应用优化,使 std::function
的参数保持在这些限制以下;例如使用指向 struct
的指针或引用(也 std::ref
)作为参数(但必须注意不要悬空)。
如果性能很重要,但您仍然想要一个具体的接口,请考虑绑定到 lambda 而不是活页夹对象(通过 std::bind)。
我 运行 这个测试是在 gcc 5.3 (libstdc++, -O2)
#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 , %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 , %esi
movl , %edi
jmp foo(int, int)
test1():
subq , %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 , %edx
movq %rsp, %rsi
movq %rsp, %rdi
call *%rax
.L7:
addq , %rsp
ret
typeinfo for test1()::{lambda()#1}:
typeinfo name for test1()::{lambda()#1}:
通过使用引用包装器,您应该避免堆分配:
auto f = std::bind(foo, 1, 2);
runThisFunction(std::ref(f));
这是因为 reference_wrapper
是一个小对象,并且鼓励 std::function
避免为小对象分配(参见 [func.wrap.func.con]):
Note: Implementations are encouraged to avoid the use of dynamically allocated memory for small callable objects, for example, where f’s target is an object holding only a pointer or reference to an object and a member function pointer.
仅当 runThisFunction()
未存储 std::function
的值以便在 f
的生命周期后调用时才有效。
非常简单(至少对于 gcc 而言):
int someFunc(int a, int b) { return a+b; }
//....
int b = (std::bind(someFunc,3,4))(); // returns 7