I'm stuck reading the description of std::bind
in N3225, in subsection 20.8.10.1
. It says the following should print 1
, but I thought that bind
is supposed to copy its arguments and therefor it should print 0
. If one wants to refer to the passed argument, one needs to use std::ref
, right?
void f(int &a) { a = 1; }
int main() {
int a = 0;
std::bind(f, a)();
std::cout << a << std::endl;
}
GCC outputs 0
, agreeing with what I thought things work. But N3225 says that std::bind(f, a1)
shall return a call wrapper that when called by wrapper()
will call INVOKE(f, v1)
, where v1
shall be a
(the argument I passed in, in other words, using binds
's incoming parameter which is a perfect forwarding parameter, std::forward<A1>(a1)
).
INVOKE(f, a)
is defined by 20.8.2 to f(a)
. So, this defines that the call to the returned call wrapper passes the original argument. What am I missing?
It says the following should print 1
No, it does not say that.
If one wants to refer to the passed argument, one needs to use std::ref, right?
Yes.
But N3225 says that std::bind(f, a1) shall return a call wrapper that when called by wrapper() will call INVOKE(f, v1), where v1 shall be a (the argument I passed in, in other words, using binds's incoming parameter which is a perfect forwarding parameter, std::forward(a1)).
That's where you are wrong. The "bound arguments" you passed in at the bind-call are stored in form of newly created objects of type TiD
which are each constructed from forward<Ti>(ti)
. This is made reasonably clear by saying "tid is an lvalue of type TiD constructed from std::forward<Ti>(ti)
". Due to the special treatment of reference wrappers, there is an additional "transformation layer". See 20.8.10.1.2/10 which explains how vi
and Vi
relate to tid
and TiD
.
It prints a 0 at present because at the point you call std::bind it doesn't know you want to pass a reference. It doesn't look at the signature of the function to see what parameter types it takes and adjust accordingly.
To make it work properly call
void f(int &a) { a = 1; }
int main() {
int a = 0;
std::bind(f, std::ref(a))();
std::cout << a << std::endl;
}
C++0x is suggesting "perfect binding" but there is a huge danger with this, which could badly break existing code silently that relies on the present behaviour. Here is a very simple example.
void myCallback( const std::string& str, int i );
function< void(int) > makeCallback( const std::string & str )
{
return bind( myCallback, str, _1 );
}
At present you can rely on bind copying the string that you pass in with str
and thus the fact it will be valid come the callback's invocation.
If it "smartly" used "perfect binding" to store it as a reference, it would break situations like this.
Wow, this is confusing beyond belief. It defines v1
as tid
and it as the following (ti
is the i-th perfect forwarding bind parameter, and TiD
is the decayed type of that parameter - i.e an array becomes a pointer etc).
tid
is an lvalue of typeTiD
constructed fromstd::forward<Ti>(ti)
Alright, I did say, this tid
is std::forward<Ti>(ti)
and it's an lvalue! But this is not what it really means to say. It means
tid
is an lvalue of typeTiD
that refers to an object constructed fromstd::forward<Ti>(ti)
It makes much more sense now. Because what if std::forward<Ti>(ti)
is actually an rvalue? The "lvalue ... constructed from ..." is meant to mean that we create a new object from "..." and make the lvalue refer to it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With