4

When I want to create an std::function for wrapping work(..) member, I had a compilation error that tired me.

sample code :

class C{ 
public:
    C(){
        std::function<void(void) > f = std::bind(&C::work,
                                                 this,
                                                 std::bind(&C::step1, this),
                                                 std::bind(&C::step2, this));
        QList<decltype(f)> lst;
        lst.append(f);
        .....
    }
private:
    void work(std::function<bool()> fn1, std::function<bool()> fn2 ) {
        if (fn1()) {
            QTimer::singleShot(1, fn2);
        }
        else {
            QTimer::singleShot(5, fn1);
        }
    }

    bool step1(){return true;}
    bool step2(){return true;}
};

Compile Error:

main.cpp:49: erreur : conversion from 'std::_Bind_helper<false, void (C::*)(std::function<bool()>, std::function<bool()>), C* const, std::_Bind<std::_Mem_fn<bool (C::*)()>(C*)>, std::_Bind<std::_Mem_fn<bool (C::*)()>(C*)> >::type {aka std::_Bind<std::_Mem_fn<void (C::*)(std::function<bool()>, std::function<bool()>)>(C*, std::_Bind<std::_Mem_fn<bool (C::*)()>(C*)>, std::_Bind<std::_Mem_fn<bool (C::*)()>(C*)>)>}' to non-scalar type 'std::function<void()>' requested
                                              std::bind(&C::step2, this));
                                                                        ^
Ken Y-N
  • 14,644
  • 21
  • 71
  • 114
Mohamed Hamzaoui
  • 325
  • 4
  • 15
  • 3
    Bind of bind is magic. It doesn't work as you might expect. – Kerrek SB May 27 '16 at 11:29
  • 3
    Use lambdas instead of bind. – Yakk - Adam Nevraumont May 27 '16 at 11:29
  • 1
    Your compilers error statement is telling you the problem, `bind` doesn't do implicit conversion, and the result of `bind(&C::step, this)` is a `_Bind_helper` *not* a `function`. You need to do the conversion yourself: `bind(&C::work, this,function(bind(&C::step1, this)), function(bind(&C::step2, this)))` [Live Example](http://ideone.com/lvVL5z) – Jonathan Mee May 27 '16 at 12:12
  • 2
    @JonathanMee That works but not for the reason you think it does. `std::bind(&C::step1, this)` itself *is* convertible to `std::function` after all. – Barry May 27 '16 at 13:28
  • when we test with functiona=std::bind(&C::step1, this), functionb=std::bind(&C::step2, this) and bind(&C::work, this, a, b) we get the same problem so I think it's not a problem of implicit conversion – Mohamed Hamzaoui May 27 '16 at 13:32
  • @Barry Of course it is convertible. I was saying the reason that the OP's code didn't work is because `bind` does not *do* the conversion. For `bind` to work the programmer must do the conversions explicitly. Or do you disagree with that statement? – Jonathan Mee May 27 '16 at 13:33
  • 1
    @requinham It's absolutely the problem. And the test code you wrote runs fine: http://ideone.com/tzifGe There may be a compiler issue or there's something else going on that you haven't shown me. – Jonathan Mee May 27 '16 at 13:38
  • @JonathanMee Yes, that's false. See my answer. Or the linked dupe. – Barry May 27 '16 at 13:39
  • @Barry And you are right, I'm wrong, per the norm +1. Thanks for setting me straight. So the reason mine works is because I've converted it so it's no longer a `bind`-subexpression. It has nothing to do with implicit conversion. – Jonathan Mee May 27 '16 at 13:47
  • @JonathanMee Exactly right. – Barry May 27 '16 at 13:48
  • @JonathanMee you are right. – Mohamed Hamzaoui May 27 '16 at 14:22
  • At least I can be right about the end result, even if it takes @Barry to correct my cause ;) – Jonathan Mee May 27 '16 at 14:25

1 Answers1

7

The problem is that bind() will eagerly evaluate nested bind expressions. So instead of ending up with some callable that returns bool (as you had intended from std::bind(&C::step1, this)), you just end up with bool.

Instead, use lambdas:

std::function<void(void) > f = [this]{
    work([this]{ return step1(); },
         [this]{ return step2(); });
};
Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    I was wondering if you knew where this issue is referenced. +1 – Mohamad Elghawi May 27 '16 at 11:37
  • @MohamadElghawi What do you mean? – Barry May 27 '16 at 11:41
  • You said: "bind() will eagerly evaluate nested bind expressions". Was just wondering where you read that. Thanks in advance. – Mohamad Elghawi May 27 '16 at 11:42
  • 1
    @Mohamad [reference](http://en.cppreference.com/w/cpp/utility/functional/bind), see behavior under `operator()` – Barry May 27 '16 at 11:51
  • @MohamadElghawi "(e.g., another bind subexpression was used as an argument in the initial call to bind), then that bind subexpression is invoked immediately as an lvalue object with the same cv-qualifications as that of g, and its result is passed to the invocable object." http://en.cppreference.com/w/cpp/utility/functional/bind#Member_function_operator.28.29 – Jonathan Mee May 27 '16 at 13:48