9

I am playing around with std::function and std::bind to understand how arguments are copied around and if I can save some of the copy operations.

I understand that when using std::bind, the arguments are passed by value and not reference (unless std::ref is specified). However, when I run the following snippet, the copy constructor is invoked twice. Can someone explain why?

struct token
{
    static int i;
    int code;

    token()
        : code(i++)
    {
        cout << __FUNCTION__ << ": " << code << endl;
    }
    virtual ~token()
    {
        cout << __FUNCTION__ << endl;
    }

    token (token const & other)
        : code (other.code)
    {
        cout << "copy ctor: " << code << endl;
    }

    // update -- adding a move ctor
    token (token const && other)
        : code (std::move(other.code))
    {
        cout << "move ctor: " << code << endl;
    }
    // update -- end

    void boo() const
    {
            cout << __FUNCTION__ << ": " << code << endl;
    }


};

void call_boo(token const & t)
{
    t.boo();
}


int main()
{
    token t2;

    cout << "default" << endl;
    std::function< void () >(std::bind(&call_boo, t2));

    cout << "ref" << endl;
    std::function< void () >(std::bind(&call_boo, std::ref(t2)));

    cout << "move" << endl;
    std::function< void () >(std::bind(&call_boo, std::move(t2)));


    cout << "end" << endl;
    return 0;
}

When run, this produces the following output:

token: 1
default
// Without move ctor
// copy ctor: 1 // Makes sense. This is the passing by value.
// copy ctor: 1 // Why does this happen?
// With move ctor    
copy ctor: 1
move ctor: 1
~token
~token
ref // No copies. Once again, makes sense.
move
// Without move ctor
// copy ctor: 1
// copy ctor: 1
// With move ctor
move ctor: 1
move ctor: 1
~token
~token
end
~token
xskxzr
  • 12,442
  • 12
  • 37
  • 77
urnonav
  • 191
  • 7
  • 1
    Did you consider [using the `gdb` debugger](https://sourceware.org/gdb/current/onlinedocs/gdb/) and putting a breakpoint to understand when the constructors get called? – Basile Starynkevitch May 27 '18 at 05:21
  • 3
    I'd guess that first it gets copied into the binder functor and then the latter gets moved into the `std::function` object. But since your `token` has no dedicated move constructor, the copy constructor is used instead. Basically, everything is as expected. – AnT stands with Russia May 27 '18 at 05:26
  • 1
    Modern Effective C++, Item 34: Prefer lambdas to std::bind . – Vorac May 27 '18 at 05:33
  • Seriously, your own question can be answered by just putting a breakpoint in the debugger and looking at the call stack. – PaulMcKenzie May 27 '18 at 05:41

1 Answers1

5

Parameters for this constructor of std::function are always copied, so a copy of the bind object (which itself has a copy of your token object) is created inside the std::function.

http://en.cppreference.com/w/cpp/utility/functional/function/function

template< class F > 
function( F f );

5) Initializes the target with a copy of f. If f is a null pointer to function or null pointer to member, *this will be empty after the call. This constructor does not participate in overload resolution unless f is Callable for argument types Args... and return type R. (since C++14)

xaxxon
  • 19,189
  • 5
  • 50
  • 80
  • The copy [is initialized with `std::move(f)`](http://www.eel.is/c++draft/func.wrap#func.con-10). cppreference.com seems to elide this clarification. – xskxzr May 27 '18 at 06:18