Unanswered Nested std::bind, compilation problem

  • 18. července 2012 6:43
     
      Obsahuje kód

    Hi

    Not sure what is the problem with the following code:

    #include <iostream>
    #include <functional>
    using namespace std;
    void foo(void) { cout << "foo()" << endl; }
    template<class F>
    void bar(F f)
    {
        f();
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        auto a = std::bind(&foo);
        auto b1 = std::bind(bar<decltype(a)>, std::ref(a));
        auto b2 = std::bind(bar<decltype(a)>, a); // <--- what's wrong here?
        b1(); // OK
        b2(); // Fails to compile
        return 0;
    }

    Error Message:

    http://pastebin.com/DGftEt0b

    Thank you!

Všechny reakce

  • 18. července 2012 13:45
     
     

    MS's implementation of TR1 has had issues with nested binds for a long time.  I know they were in VS2008 and IIRC, people have mentioned issues in VS2010 as well.  I don't know if there are problems in VS2012 or if they were fixed. 

    Since you are using VS2010, why not just use a lambda instead?  It will probably be easier to read and maintain.  You could also try using a std::function for a instead of a std::bind.

  • 18. července 2012 14:49
     
     

    No, I'm using VC2012. Sorry, I didn't mention that.

    And I'm having the same issue in GCC 4.7.0.

    Yes, I know I can implement that in a different way.  But I don't understand what's wrong with that code.

    <functional> header is quite complex and I'm having problems tracing this issue. :(

  • 18. července 2012 17:09
     
     
    Not sure what the problem is.  GCC doesn't seem to like it either.
  • 19. července 2012 9:36
     
     
    • Upravený i1friend 19. července 2012 10:55
    •  
  • 15. března 2013 2:33
     
      Obsahuje kód

    I don't think it's an issue of c++ compiler or library. On contrast, it works as same as what c++ standard describes, which can be found in std::bind manual.

    Basically, std::bind comes from boost::bind and its document has given a detailed section for nested binding.

    Then come back to your code. From the document, the inner bind() will be evaluated first. So, this code sipper

    auto a = std::bind(&foo);
    auto b2 = std::bind(bar<decltype(a)>, a);
    

    will be compiled like

    auto b2 = std::bind(bar<decltype(std::bind(&foo))>,
                        std::bind(&foo)());
    

    Now compiler tried to choose bar(decltype(std::bind(&foo)) f), but found that the real type of f here is 'void', which is return type of std::bind(&foo)(). Then, it complained.

  • 15. března 2013 3:44
     
     

    On 3/14/2013 10:33 PM, yayj wrote:

    auto a = std::bind(&foo);
    auto b2 = std::bind(bar<decltype(a)>, a);

    Note how this code never calls foo()

    will be compiled like

    auto b2 = std::bind(bar<decltype(std::bind(&foo))>,
                                             std::bind(&foo)());

    ... while this code does. What makes you believe the compiler can legally rewrite code in a way that introduces a function call that wasn't there before?


    Igor Tandetnik

  • 18. března 2013 14:58
     
      Obsahuje kód

    No, the programmer really doesn't call foo(). But the implicit semateme of nested std::bind will do.

    Now, I'm going to cite the code in std::bind reference from cppreference.com:

    void f(int n1, int n2, int n3, const int& n4, int n5)
    {
        std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
    }
     
    int g(int n1)
    {
        return n1;
    }
    
    int main()
    {
        // omitted code
    
        // nested bind subexpressions share the placeholders
        auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);
        f2(10, 11, 12);
    
        // omitted code
    }

    Look at the second parameter of f(), its type is int instead of decltype(std::bind(g, _3)) and the return type of g is also int. Coincidence huh, :) If the parameter were decltype(std::bind(g, _3)), the compile would complain because of no such function.

    And here's the quotation from boost::bind document to explain this code sippet:

    bind(f, bind(g, _1))(x);               // f(g(x))

    <cite>The inner bind expressions are evaluated, in unspecified order, before the outer bind when the function object is called; the results of the evaluation are then substituted in their place when the outer bind is evaluated. In the example above, when the function object is called with the argument list (x), bind(g, _1)(x) is evaluated first, yielding g(x), and then bind(f, g(x))(x) is evaluated, yielding the final result f(g(x)).</cite>

    I would have to say sorry because my account can't paste the hyperlink in my post. Please re-check the documents on their real site.


    • Upravený yayj 18. března 2013 15:36 add the quotation of boost::bind document
    •  
  • 18. března 2013 15:43
     
     

    On 3/18/2013 10:58 AM, yayj wrote:

    No, the programmer really doesn't call foo(). But the implicit semateme of nested std::bind will do.

         // nested bind subexpressions share the placeholders
         auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);
         f2(10, 11, 12);

    Look at the second parameter of f(), its type is int instead of decltype(std::bind(g, _3))

    Ah, I see what you are saying. Yes, you are right and I am wrong. std::bind has a special case for when one of its parameters is itself an object produced by bind (one for which is_bind_expression reports true). Rather than using the object itself as an argument to the wrapped functional, it invokes the nested bind and uses the result of that invocation as an argument. So one cannot build second-order predicates from bind expressions alone.

    Wrapping the bind-produced object into reference_wrapper suppresses this behavior. This is why, in the OP's code, std::bind(..., std::ref(a)) works while std::bind(..., a) doesn't. In the former case, a reference to 'a' itself is being passed to bar(), while in the latter, the result of invoking a() is being passed.


    Igor Tandetnik