13

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?

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • http://stackoverflow.com/questions/4327474/does-perfect-forwarding-in-c0x-make-reference-wrapper-deprecated (Not a duplicate but related, whether we will get "perfect forwarding" in C++0x) – CashCow Feb 21 '11 at 13:26

3 Answers3

5

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.

sellibitze
  • 27,611
  • 3
  • 75
  • 95
  • To me, saying that an lvalue of `TiD` is constructed from `forward(ti)` means that `forward(ti)` shall be an lvalue or that it shall be casted to `TiD&` as in `(TiD&)forward(ti)` to be an lvalue. There is no way I could read it as saying that it shall create a new object that the lvalue refers to. Because in such a case, the lvalue would be constructed by naming that new object. IMO that's a very confusing way stating it. – Johannes Schaub - litb Feb 21 '11 at 20:46
  • "construction" implies "object". In `(TiD&)forward(ti)` there is no object construction taking place. An lvalue reference is not an object type. – sellibitze Feb 22 '11 at 08:27
  • "constructed" can mean other things than creating objects. Compare to "C++ program constructed according to the syntax rules [...]" (definition of "well-formed"). "to construct" used together with "construct an lvalue" does not near mean to create an object. To construct an lvalue can only mean what it says, not something else. The next code constructs an lvalue from the identifier 'x': `int main() { int x = 0; x; }`. In the next code, *no lvalue is constructed* `int main() { int x = 0; }`. – Johannes Schaub - litb Feb 22 '11 at 13:35
  • Sure, you can use "construced" more loosely in other ways. I was talking about "construction" in the way it is used in the type trait class `is_constructible`, for example. If you think the draft needs clarifications in that area, propose an update. I for one consider this section perfectly adequate. – sellibitze Feb 22 '11 at 19:23
  • @sellibitze the description of `is_constructible` at 20.7.4.3p6 looks perfectly adequate. It does not say such things as "true if a T lvalue can be constructed from an U lvalue". But it words it in terms of a temporary variable. – Johannes Schaub - litb Feb 23 '11 at 11:17
  • I was talking about the link between the word "construction" and "object" which is compatible to how these concepts are used is_constructible about. I didn't mean to imply anything else. I still consider "lvalue constructed from" to be just a short cut of your "lvalue referring to an object that has been constructed" because an lvalue always refers to some object that has been constructed. – sellibitze Feb 26 '11 at 13:01
3

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.

CashCow
  • 30,981
  • 5
  • 61
  • 92
  • 2
    The original poster clearly stated he does know to use `std::ref`. He asked for how the specification should be interpreted, *not* how to make the code print 1, so this is not an answer. – Jan Hudec Feb 21 '11 at 14:21
  • I have now answered why not forcing explicit std::ref could be very dangerous. Not sure it directly answers the question and I really do not understand all these tid things... – CashCow Feb 24 '11 at 16:46
2

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 type TiD constructed from std::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 type TiD that refers to an object constructed from std::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.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212