0

I am having trouble with std::bind and using a moveable type as a parameter. Is read this and this, but in those cases I actually see the issue since there the OP want's to have a rvalue reference inside the bind. What I want to is have a lvalue, but just to move-construct it when calling std::bind. Here is a minimal sample:

#include <functional>

struct Foo
{
    using Callback = std::function<void(int)>;

    void Do(const Callback &callback)
    {
        callback(5);
    }
};

class Param
{
private:
    Param() {};

public:
    Param(Param&&)
    {
    }

    // commenting out copy ctor triggers the last two invocations in Bar() to fail
#if 1   
    Param(const Param&)
    {
    }
#endif

    Param& operator=(Param&&)
    {
        return *this;
    }

    static Param Create()
    {
        return Param();
    }

    int value_ = 10;
};

struct Bar
{
    Bar()
    {
        Foo f;
        f.Do(std::bind(&Bar::Callback1, this, std::placeholders::_1));
        Param p(Param::Create());
        f.Do(std::bind(&Bar::Callback2, this, std::move(p), std::placeholders::_1)); // does not work w/o copy ctor
        f.Do(std::bind<decltype(&Bar::Callback2), Bar*, Param>(&Bar::Callback2, this, std::move(p), std::placeholders::_1)); // does not work w/o copy ctor
    }

    void Callback1(int a)
    {
        printf("%d\n", a);
    }

    void Callback2(const Param &p, int a)
    {
        printf("%d %d\n", a, p.value_);
    }
};

int main()
{
    Bar b;
}

So I want Param to be move constructible - as shown in the comments adding a copy ctor solves everything (and if everything else fails I will just go that way). The only thing that from my point of view could prohibit this is if the result of std::bind must be copy constructible, but cppreference states:

The return type of std::bind is CopyConstructible if all of its member objects (specified above) are CopyConstructible, and is MoveConstructible otherwise.

I thought that if the type how the variable is stored in the bind is deduced from the call then std::move could cause an attempt to store a rvalue reference, thus the second call where I explicitly state the template parameters (and would expect the bind itself to have a member of type Param which is move constructible and move assignable).

What am I missing here?

Rudolfs Bundulis
  • 11,636
  • 6
  • 33
  • 71

1 Answers1

1

Your troubles come from std::function.

std::bind returns some callable which has Param as member variable. When std::function instance is created all members of that functor generated by bind are stored by copying. When copy constructor of Param is commented out, it is just not possible to create std::function wrapper. And this is your issue.

As solution, get rid of std::function, and just pass any callable as template parameter:

template<class F>
void Do(F&& callback)
{
    callback(5);
}

Live demo

rafix07
  • 20,001
  • 3
  • 20
  • 33
  • Thanks for the answer, sadly, I can't use this approach, since I actually have a class that takes a std::function callback, and I just wanted to bind my member function with extra parameters as that callback. I'll just adjust it to become copyable, still this seems weird. Is there any chance std::function will be improved to allow this? – Rudolfs Bundulis Mar 30 '20 at 10:15