Search code examples
c++c++11boost

boost::bind() creates many copies of the argument


I was working with boost::bind (Boost 1.64.0 and gcc 8.3.0) to create a callable object and noticed an interesting feature An object passed in bind constructor as an argument to a wrappable function is copied multiple times. Even if the object is wrapped in std::move(). Although std::bind works as expected. Example:

#include <iostream>
#include <boost/bind.hpp>
#include <functional>

class Test
{
public:
    Test()
    {
        std::cout << "Create\n";
    }

    Test(const Test& rhs)
    {
        std::cout << "Copy\n";
    }

    Test(Test&& rhs)
    {
        std::cout << "Move\n";
    }

    ~Test() noexcept
    {

    }
};

void foo(Test& t)
{

}


int main()
{
    Test t;
    auto f = boost::bind(&foo, t);
    f();
}

Output for boost::bind(&foo, t);

Create
Copy
Copy
Copy
Copy
Copy

Output for boost::bind(&foo, std::move(t));

Create
Move
Copy
Copy
Copy
Copy

Output for std::bind(&foo, t);

Create
Copy

Output for std::bind(&foo, std::move(t));

Create
Move
  • Why does boost copy so many times?
  • Is it correct to pass rvalue as an argument to bind (in both cases of implementation)?
  • Do I understand correctly that bind will move the object to its context and store it, and when foo is called, passes it as an lvalue reference?

Thank you!


Solution

  • That's by design.

    To avoid it, avoid copies of the bind adaptor and use ref:

    auto f = boost::bind(&foo, boost::ref(t));
    

    • Why does boost copy so many times?

    Mostly due to the fact that your constructor cannot be elided. Keep it an aggregate or trivial constructor and it doesn't happen.

    • Is it correct to pass rvalue as an argument to bind (in both cases of implementation)?

    Yes, bind captures arguments by value (unless you use explicit ref() or cref() to create reference_wrappers.)

    • Do I understand correctly that bind will move the object to its context and store it, and when foo is called, passes it as an lvalue reference?

    Yes


    Demo

    Live On Coliru

    #include <boost/bind.hpp>
    #include <functional>
    #include <iostream>
    
    struct Test {
        Test()                       { std::cout << "Create\n"; } 
        Test(const Test& /*unused*/) { std::cout << "Copy\n";   } 
        Test(Test&& /*unused*/)      { std::cout << "Move\n";   } 
        ~Test() noexcept             {                          } 
    };
    
    void foo(Test& /*unused*/) {}
    
    int main() {
        Test t;
        auto f = boost::bind(&foo, boost::ref(t));
        f();
    }
    

    Prints:

    Create