What is the rationale behind std::bind and std::thread always copying arguments?

前端 未结 5 1413
你的背包
你的背包 2021-02-20 03:35

It\'s pretty well known that the default behaviour of std::bind and std::thread is that it will copy (or move) the arguments passed to it, and to use reference semantics we will

5条回答
  •  情歌与酒
    2021-02-20 04:16

    I did in fact write a small utility that creates a delayed invocation functor (somewhat std::bind-like, but without the nested bind expressions/placeholders features). My main motivation was this case that I found counter-intuitive:

    using pointer_type = std::unique_ptr;
    pointer_type source();
    void sink(pointer_type p);
    
    pointer_type p = source();
    
    // Either not valid now or later when calling bound()
    // auto bound = std::bind(sink, std::move(p));
    auto bound = std::bind(
        [](pointer_type& p) { sink(std::move(p)); }
        , std::move(p) );
    bound();
    

    The reason for that adaptor (which moves its lvalue ref argument to sink) is that the call wrapper return by std::bind always forwards the bound arguments as lvalues. This wasn't a problem with e.g. boost::bind in C++03 since that lvalue would either bind to a reference argument of the underlying Callable object or to a value argument via a copy. Doesn't work here since pointer_type is move-only.

    The insight that I got is that there really are two things to consider: how the bound arguments should be stored, and how they should be restored (i.e. passed to the Callable object). The control that std::bind grants you is as follows: arguments are either stored in a shallow (via the use of std::ref) or regular manner (using std::decay with perfect forward); they are always restored as lvalues (with cv-qualifiers inherited from the owning call wrapper). Except that you can bypass the latter with a small on-site adaptor lambda expression like I just did.

    It's arguably a lot of control and a lot of expression for relatively little to learn. In comparison my utility has semantics like bind(f, p) (decay and store copy, restore as lvalue), bind(f, ref(p)) (store shallowly, restore as lvalue), bind(f, std::move(p)) (decay and store from move, restore as rvalue), bind(f, emplace(p)) (decay and store from move, restore as lvalue). This feels like learning an EDSL.

提交回复
热议问题