C++ Function bind repeating arguments to curried function

前端 未结 2 647
你的背包
你的背包 2021-01-18 04:13

I am trying to understand the concept of currying and calling a function which concats three strings but by passing only two strings and using the second argument twice.

相关标签:
2条回答
  • 2021-01-18 04:53

    Copying strings is expensive. Since std::bind thinks that the values of the placeholders are only used once, it performs a std::move on the strings. This is done for each Parameter and as a consequence, either b or c is a moved, that means empty string.

    You can change that behavior by explicitly saying what you mean, by passing the arguments by const-reference:

    string concatthreestrings(string const& a,string const& b,string const& c)
    

    Now, it should work.

    0 讨论(0)
  • 2021-01-18 05:12

    I did a few tests using this smaller example that exhibits the same behavior you have:

    #include <functional>
    #include <iostream>
    #include <string>
    
    using std::string;
    
    void print(string s1, string s2)
    {
        std::cout << s1 << s2 << '\n';
    }
    
    int main()
    {
        using namespace std::placeholders;
    
        typedef std::function< void(string) > fn_t;
    
        fn_t func = std::bind(print, _1, _1);
    
        std::string foo("foo");
        func(foo);
    }
    
    // outputs: foo
    

    Note that I defined a string object named "foo" instead of using string literals. The behavior is the same, so the problem is not related to this.

    I think the problem comes from your typedef. The return of bind (which is unspecified) is casted to a function taking a string by value, while the wrapper returned by bind probably take its arguments by rvalue-reference and perfectly-forward them. Instead of using your own typedef, you should use the auto keyword, so that the type of func will be automatically deduced by the compiler. If we modify the main as follows, we obtain the expected behavior:

    int main()
    {
        using namespace std::placeholders;
    
        auto func = std::bind(print, _1, _1);
    
        std::string foo("foo");
        func(foo);
    }
    
    // outputs: foofoo
    

    Another solution is to replace your typedef so that func takes its parameter by reference-to-const:

    typedef std::function< void(string const &) > fn_t;
    

    I don't really understand why the other typedef does not work... Presumably the string is moved, as @ipc noted, but I don't know at what point of the execution this happens. I'm not even sure this is standard behavior, since both function and the wrapper returned by bind should use perfect forwarding. Perhaps GCC includes some optimizations that move the wrapper arguments when they are passed by value?

    Edit

    I did some tests, it turns out GCC's implementation of std::function performs a move on its arguments, while the wrapper return by std::bind does not. I still don't know if this is standard, I'm going to write a question about that.

    0 讨论(0)
提交回复
热议问题