作者Caesar08 (Caesar)
看板C_and_CPP
标题[问题] bind object(std::bind)内部用copy?
时间Wed Jul 13 22:48:49 2016
如题
先定义一下名词
bind指的是std::bind
bind object指的是std::bind的return value(standard也没说他叫甚麽名字,所以我就取这名字了)
g指的是bind object实际会呼叫的function(也就是bind的第一个parameter)
根据C++ standard
如果传给bind的是reference_wrapper,那最後会用reference的方式传给g
可是如果传给bind的不是reference_wrapper(同时也不是bind object与placeholder)
那就会先把argument copy到bind object里面
而且在bind object呼叫g时,这些data会以l-value的方式传给g
这样就会有两个问题
1.
struct A{};
void test_cpy(A){}
void test_ref(A &){}
int main()
{
A a;
bind(test_cpy,a)(); //copy
2次
bind(test_cpy,ref(a))(); //copy 1次
bind(test_ref,a)(); //copy
1次
bind(test_ref,ref(a))(); //no copy
bind(test_cpy,move(a))(); //move 1次,copy
1次
}
可是实际上,改良後的bind可以是
bind(test_cpy,a)()用reference(bind object)与copy(test_cpy)来取代2次copy
bind(test_ref,a)()用reference(bind object)与reference(test_cpy)来取代1次co
py
bind(test_cpy,move(a))()可以改用move 2次
也可以是
bind(test_cpy,a)()用copy(bind object)与move(test_cpy)来取代2次copy
bind(test_cpy,move(a))()可以改用move 2次
(以上两种bind是不同的实作方式)
不论是哪种改良後的bind,都比原本的好
为什麽bind要设计成这样?这样效能不是有很大问题吗?
(题外话:bind是从boost那边来的)
2.
void test_mov(A &&){}
int main()
{
A a;
bind(test_mov,move(a))(); //这边打什麽,compile都不过通过
}
原因很简单,bind object在呼叫g的时候,data会用l-value的方式传给g
结果现在g的parameter是A &&
解法就是把test_move(A &&)改成别的,但是这解法对很多已存在的function是行不通的
所以,bind为什麽要设计成这样呢?
(bind被引进到standard是C++11的时候,但是C++11最大的改革之一就是move,难道委员
会
我唯一想到的可能原因,就是,
bind是要拿来copy整个g的呼叫方式(可以拿来当callbac
k?
可是就算是这原因,那呼叫g的时候
非reference_wrapper的data且
g的parameter非l-value reference时也应该用move才对啊
到底是什麽原因,导致bind有这样奇怪的行为呢?
可能的解法在
#1NXvUCDP
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 140.114.233.71
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/C_and_CPP/M.1468421331.A.DE0.html
1F:→ PkmX: 1你要考虑bind的结果被呼叫前a被修改的情况07/14 01:26
对,所以我底下有提到应付这情况的做法
就是先copy,然後再move
2F:→ PkmX: 或是同一个bind的结果被呼叫多次的情况啊07/14 01:26
我不懂为甚麽要呼叫多次bind object?
更何况bind object本身可以copy与move,为甚麽不是user自己处理多次呼叫的问题?
如果要最大化效能,bind不就应该只考虑呼叫一次的情况吗?
3F:→ PkmX: 2的话要写成07/14 01:44
4F:→ PkmX: bind(test_mov, bind(move<A&>, move(a)))();07/14 01:46
忘了还有这方法,但是这方法有点丑...
5F:→ PkmX: 但是这个结果就只能呼叫一次07/14 01:47
6F:→ cplusplus: 确实bind传回functor是要能之後用呀,不然要它干嘛?XD07/14 02:38
7F:→ cplusplus: 所以没办法用最後提到A&&那个也很正常,不太合使用情境07/14 02:41
如果只呼叫一次,情境就很多了
8F:→ PkmX: 其实我觉得有了lambda以後 bind就很少用了XD07/14 02:41
如果你必须马上copy argument,那lambda就不能用了
考虑到parameter pack,你要怎麽copy?
(不过听说可以用tuple解决,但我不会用tuple XD)
9F:→ cplusplus: 如上面仁兄所说,一般用rval ref传入只能用一次,参数07/14 02:42
10F:→ cplusplus: 通常就被修改掉了(如果不会也没需要用rval ref当参数) 07/14 02:43
11F:→ cplusplus: SORRY切断了。同意多用lamda,好处多多XD07/14 02:44
12F:→ cplusplus: 少了b07/14 02:46
13F:→ PkmX: 是我切断你了XD C++14可以做move capture 2就可以直接写成 07/14 02:46
14F:→ PkmX: [a{move(a)}]() mutable { test_mov(move(a)); }();07/14 02:47
15F:→ cplusplus: 先撇开bind不说,想了一下还想不到需要这样用的情境~07/14 03:06
16F:→ cplusplus: 不知原有啥用到的例子吗??07/14 03:07
假设你用bind object传给另一个已经在执行的thread(但目前被暂停)
而原本的g为了效能考量,parameter是用&&
结果你却必须改变g的signature才能达到这功能
17F:→ cplusplus: smart pointer应用?07/14 03:25
18F:→ PkmX: 多次呼叫你可以考虑类似generator的应用07/14 11:51
19F:→ PkmX: auto f = std::bind([](int& n) { return n++; }, 0); 07/14 13:33
20F:→ PkmX: f(); f(); f(); 07/14 13:33
那这样我可以理解
也就是bind应用的场合在
1. 需要马上copy argument时(以便之後的callback)
2.
会多次呼叫bind object(所以不接受r-value reference)
但是我想到的应用场合是
1. 需要马上copy argument时(以便之後的callback)
2.
只会呼叫1次bind object(所以需要perfect forwarding)
那这样看来,我可能要自己写一个bind object了
(不然就是要传bind object给bind,但是这样很丑...)
21F:→ PkmX: 用lambda吧XD [=]() mutable { test_mov(std::move(a)); } 07/14 14:40
考虑这个
template<class Func,class ... Args>
void test(Func &&func,Args &&...args)
{
bind(std::forward<decltype(func)>(func)
,std::forward<decltype(args)>(args)...);
}
如果用lambda,那
template<class Func,class ... Args>
void test(Func &&func,Args &&...args)
{
[???]{
std::forward<decltype(func)>(func)
(std::forward<decltype(args)>(args)...);
};
}
lambda没办法解决这问题
22F:→ PkmX: 如果 ??? 是 = 就和 std::bind 是一样的啊 07/14 16:25
对 你突破盲点了 XD
如果用lambda时有用ref,那=就会跟bind一样
23F:→ PkmX: 可是bind那样写也是没办法处理func是吃rvalue ref的情况 07/14 16:26
25F:→ PkmX: 这个版本用lambda可以达到你要的效果 建立时先复制args一次 07/14 17:18
26F:→ PkmX: 然後因为只用一次 他会直接把复制的args move给func 07/14 17:19
我有试过这做法
但是好像会遇到其他问题...
27F:→ PkmX: 但若func要lvalue ref 会用template版本转成lvalue ref给它 07/14 17:20
28F:→ cplusplus: C++提供很多机制可以用,但怎麽用也蛮重要,有时候是 07/14 17:21
29F:→ PkmX: 虽然我觉得arg_ref_t改一改也可以给bind用 07/14 17:21
30F:→ cplusplus: 设计上的问题,不一定非用不可,就像数值类型的class 07/14 17:21
31F:→ cplusplus: 要把op+-*/复写成完全不相关的操作也行啊,但不是好事 07/14 17:22
32F:→ cplusplus: 所以才想知道原PO的情境跟用途。而且确实bind出来的东 07/14 17:22
我要的就是尽可能减少copy次数,因为现在的bind需要将data copy给g
但我不需要可重复呼叫的bind object,所以现在的bind不符合我的要求
33F:→ cplusplus: 西,蛮多都是把一些设定用的参数用好方便後面一直呼叫 07/14 17:23
34F:→ cplusplus: 有些书本提到,如果move不昂贵,有时候多几次move可能 07/14 17:24
35F:→ cplusplus: 产生的效能成本拿来换程式维护/易用性可能更划算 07/14 17:26
36F:→ cplusplus: 所以好奇原PO想怎麽用~ 还有能用lambda就用,效能有机 07/14 17:27
我不是不用lambda
是我要马上copy argument,所以lambda不能达到我的要求(因为我没想到用lambda+ref)
37F:→ cplusplus: 可提升/最佳化的机会多许多(ex,function inline) 07/14 17:28
38F:→ cplusplus: 话说use_lambda可能也要考虑f不是function是functor的 07/14 17:34
39F:→ cplusplus: 可能性,不然拿回来的lambda呼叫时可能会炸掉~ 07/14 17:35
40F:→ cplusplus: 唔,看错了,昏... 07/14 18:26
41F:→ PkmX: 查了一下Scott Meyers认为C++14以後已经没有使用bind的必要 07/14 20:11
42F:→ PkmX: C++14对lambda新增的features让他可以完全取代bind的功能 07/14 20:11
是
如果lambda+ref,那就可完全取代bind了
43F:→ PkmX: 所以只是看用哪个写比较好懂好维护这样 07/14 20:13
※ 编辑: Caesar08 (140.114.233.71), 07/19/2016 01:17:52