1

I've been trying to use boost::bind to post a call to a member function onto an io_strand but have been getting errors. I have manged to create a simplistic equivalent example of what I am trying to do and have seen the same error in the following context:

I have the following class containing the doThings() member function I want to call:

class base
{
public:
  int x = 1;

  template<typename B>
  void doThings(B&& b)
  {}
};

There is then a subclass of this (to accurately represent the scenario I am encountering my error - I don't think it makes a difference)

class child : public base
{
  int y = 2;
};

I have the following code trying to make the boost::bind call:

template<typename A>
void getInt(child c, A&& a)
{
  boost::bind((void(child::*)(A)) &child::doThings, &c, std::forward<A>(a))();
}

And then this is being called as follows:

int main()
{
  child c = child();
  getInt(c, 7);
}

When I compile the above code I get the following error:

error: no matches converting function ‘doThings’ to type ‘void (class child::*)(int)’


If I change the function signature of doThings() to take a regular B type rather than a universal reference i.e. B rather than B&& then it compiles a runs with no issues.
I suspect my issue is something to with the cast I'm doing in getInt():

(void(child::*)(A))

But I don't know what I would need to change it to. A&& wouldn't work in this context as I believe it would represent an r-value reference in that context. The compilation error I get when I try it seems to confirm this:

error: cannot bind ‘int’ lvalue to ‘int&&’

For completeness: if I don't attempt to perform a cast then I get the following compilation error:

error: no matching function for call to ‘bind(unresolved overloaded function type, child*, int)’

Could someone please enlighten me on what I would need to do in order to make my boost::bind call valid in this scenario please?

I am using C++11

zephyrJ
  • 58
  • 6
  • You know that you're passing an `int` to `doThings` right? – Jonathan Mee Jan 25 '17 at 16:20
  • You might use lambda with *simpler* syntax: `[&](){ c.doThings(std::forward(a));};`. – Jarod42 Jan 25 '17 at 16:25
  • @Jonathan Mee Yes, is that an issue? I realize now that the getInt is an odd choice of name here - I had been messing around with various permutations for the last our or so and the fact that the function has nothing to do with getting an int has slipped through my thinking. The name is irrelevant to the intention here – zephyrJ Jan 25 '17 at 16:26
  • Any reason to not do `c.getThings(std::forward(a));`? – Barry Jan 25 '17 at 16:35

1 Answers1

2

I would suggest against using boost::bind, as lambda expressions can be used to cleanly bind arguments (avoiding many pitfalls of bind explained in this talk by STL).


I'm assuming that you want to:

  • Capture a by move if an rvalue-reference is passed to getInt.

  • Capture a by reference if an lvalue-reference is passed to getInt.

I'm also assuming that:

  • A is not int in your real code, otherwise perfect-forwarding would not make sense.

  • You want to avoid unnecessary copies of a or that A could be a move-only type.

  • You only have access to C++11 (and not to newer standards).

If you need to "perfectly-capture" a (i.e. capture-by-move if A is an rvalue-reference, capture-by-reference if A is an lvalue-reference), you need some sort of wrapper.

Unfortunately this is non-trivial, even though it gets better in C++14 and C++17. Here's an example of how the final syntax could look:

template<typename A>
void getInt(child c, A&& a)
{
    // `a_wrapper` stores an `A&` or an `A` depending on the value category
    // of `a`. Copying it should not copy `a` - it should conditionally move 
    // it depending on the original value category of `a`.
    auto a_wrapper = forward_capture_wrapper(std::forward<A>(a));

    // `forward_like` retains information about `a`'s value category so that
    // it can be used in the body of the lambda to forward the reference/value
    // stored inside `a_wrapper`.
    //                          vvvvvvvvvvvvvvv
    [&a, a_wrapper]{ c.doThings(forward_like<A>(a_wrapper.get()); }();
    //                                          ^^^^^^^^^^^^^^^
    // `a_wrapper.get()` returns a reference that can then be moved or passed
    // to `c.doThings`.
}

As you can see, you need a template function called forward_capture_wrapper that deals with "perfect-capture". You can find information on how to implement that in these resources:

By combining the resources above you should be able to implement a "perfect capture wrapper" in C++11.

You also need a forward_like helper function that preserves the original value category of the a argument. You can find an implementation of that:

Community
  • 1
  • 1
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • Both of your assumptions are correct. Sorry, I realize my usage of int here was misleading in retrospect – zephyrJ Jan 25 '17 at 16:38