Nested bind expressions

后端 未结 2 484
终归单人心
终归单人心 2021-02-05 18:53

This is a followup question to my previous question.

#include 

int foo(void) {return 2;}

class bar {
public:
    int operator() (void) {retu         


        
2条回答
  •  遇见更好的自我
    2021-02-05 19:38

    At the beginning, please let me introduce 2 key points:

    • a: When using nested std::bind, the inner std::bind is evaluated first, and the return value will be substituted in its place while the outer std::bind is evaluated. That means std::bind(f, std::bind(g, _1))(x) executes as same as f(g(x)) does. The inner std::bind is supposed to be wrapped by std::ref if the outer std::bind wants a functor rather than a return value.

    • b: The r-value reference cannot be correctly forwarded to the function by using std::bind. And the reason has already been illustrated in detail.

    So, let's look at the question. The most importance function here might be func_wrapper which is intended to perform 3 purposes:

    1. Perfect forwarding a functor to doit function template at first,
    2. then using std::bind to make doit as a closure,
    3. and letting func function template execute the functor returned by std::bind at last.

    According to point b, purpose 1 cannot be fulfilled. So, let's forget perfect forwarding and doit function template has to accept a l-value reference parameter.

    According to point a, purpose 2 will be performed by using std::ref.

    As a result, the final version might be:

    #include 
    
    int foo(void) {return 2;}
    
    class bar {
    public:
        int operator() (void) {return 3;};
        int something(int a) {return a;};
    };
    
    template  auto func(C&& c) -> decltype(c()) { return c(); }
    
    template  int doit(C&/*&*/ c)    // r-value reference can't be forwarded via std::bind
    {
        return c();
    }
    
    template  void func_wrapper(C&& c)
    {
        func(std::bind(doit,
                       /* std::forward(c) */ // forget pefect forwarding while using std::bind
                       std::ref(c)) // try to pass the functor itsself instead of its return value
            );
    }
    
    int main(int argc, char* argv[])
    {
        // call with a function pointer
        func(foo);
        func_wrapper(foo);  // error disappears
    
        // call with a member function
        bar b;
        func(b);
        func_wrapper(b);
    
        // call with a bind expression
        func(std::bind(&bar::something, b, 42));
        func_wrapper(std::bind(&bar::something, b, 42)); // error disappears
    
        // call with a lambda expression
        func( [](void)->int {return 42;} );
        func_wrapper( [](void)->int {return 42;} );
    
        return 0;
    }
    

    But, if you really want to achieve purpose 1 and 2, how? Try this:

    #include 
    #include 
    
    void foo()
    {
    }
    
    struct bar {
        void operator()() {}
        void dosomething() {}
    };
    
    static bar b;
    
    template 
    void run(Executor&& e)
    {
        std::cout << "r-value reference forwarded\n";
        e();
    }
    
    template 
    void run(Executor& e)
    {
        std::cout << "l-value reference forwarded\n";
        e();
    }
    
    template 
    auto func(Executor&& e) -> decltype(e())
    {
        return e();
    }
    
    template 
    struct dispatcher_traits {
        enum { value = b };
    };
    
    template 
    class dispatcher {
    private:
        static void dispatch(Executor& e, dispatcher_traits)
        {
            run(e);
        }
    
        static void dispatch(Executor& e, dispatcher_traits)
        {
            run(std::ref(e));
        }
    
    public:
        static void forward(Executor& e)
        {
            dispatch(e, dispatcher_traits());
        }
    };
    
    template 
    void func_wrapper(Executor&& e)
    {
        typedef dispatcher::value>
            dispatcher_type;
    
        func(std::bind(&dispatcher_type::forward, std::ref(e)));
    }
    
    int main()
    {
        func_wrapper(foo);   // l-value
        func_wrapper(b);  // l-value
        func_wrapper(bar());  // r-value
        func_wrapper(std::bind(&bar::dosomething, &b));  // r-value
        func_wrapper([](){});  // r-value
    }
    

    Let me explain some points:

    • To reduce lots of return statements, changing functor signature from int() to void().
    • The 2 run() function templates are used to check whether the original functor parameter is perfect forwarded or not.
    • dispatcher_traits is going to map bool constant to type.
    • You'd better name dispatcher::forward to differ from dispatcher::dispatch or you have to invoke std::bind template with dispatcher::forward's signature.

提交回复
热议问题