Boost logo

Boost :

From: Gabriel Dos Reis (gdr_at_[hidden])
Date: 2004-03-08 11:40:22


Jaakko Jarvi <jajarvi_at_[hidden]> writes:

| On Sun, 8 Mar 2004, Gabriel Dos Reis wrote:
|
| > Jaakko Jarvi <jajarvi_at_[hidden]> writes:
| >
| > | My example was a bit contrived too.
| > | Here's another example (with imaginary syntax):
| > |
| > | auto foo() {
| > | int i;
| > | return auto(int x) { return i + x; }
| > | }
| >
| > Is it different from
| >
| > int& f() { int i; return i; }
| >
| > ?
| >
| >
| > In -a- model I discussed a while ago with the second author of
| > decltype/auto proposal, lambdas are unnamed constants that can be
| > implemented by local classes. The body of the lambda would be
| > forwarded-to by the appropriate overload of the call operator,
| > i.e. your example would be
| >
| > foo: () -> auto {
| > int i;
| >
| > struct xxx {
| > operator(): (int x) -> auto { return i + x; }
| > };
| > };
| >
| > That is a very simple model that just provides a syntactic sugar
| > (modulo use of auto in return type) for what you can do in current
| > C++. In particular, it is clear that the reference to "i" is a hard
| > error by application of current rules governing local classes.
|
| Right, variables with local storage cannot be used in local classes.
| Now this rule would forbid the above (which is good), but it would also
| rule out useful and safe lambdas:

Those who are considered safe would have markers sayign they are safe
to export, or they would have markers saying only their values are
needed.

|
| auto foo(int x, int y) {
| int i = x+y; // just some computation to motivate a local variable
| vector<int> v;
| ...
| transform(v.begin(), v.end(), v.begin(),
| auto(int x) { return x+i; });

Hmm, I do not see the difference between the "i" here and the "i"
above. They are both automatic.

But, if you want to tell the compiler that it is OK to use the value
of a local variable then use lambda lifting, e.g.

    transform(v.begin(), v.vend(), v.begin()
              (auto x) ((int y) auto { return y + x; }) (i));

which has the same effect as

    struct xxx {
       const int x;
       xxx(int i) : x(i) { }

       operator() const: (int y) auto { return y + x; }
    };

    transform(v.begin(), v.end(), v.begin(), xxx(i));
    

If you wanted to use the storage,

    int accu = 0;
    for_each(v.begin(), v.end(),
             (auto& s) ((int x) void { s += x; }) (accu));

which has the same effect as:

    struct xxxx {
       int& s;
       xxx(int& s) : s(s) { }
     
       operator(): (int x) void { s += x; }
    };

    for_each(v.begin(), v.end(), xxxx(accu));

| Which you can currently write using BLL as:
|
| transform(v.begin(), v.end(), v.begin(), _1 + i);
|
| (BLL stores a copy to i internally)

what about

   struct stream {
        // ...
        int send(message);
     private:
       stream(const stream&); // not implemented, prevent copy
       stream& operator=(const stream&); // ditto;
   };

    stream out("127.0.0.1:6001");
    // ...
    for_each(v.begin(), v.end(), out.send(message(_1)));

? Does it copy out?

The usual pass-by-value semantics would be still possible, modulo
explicit annotation. Plus, you would have the choice to say explicitly
const ref vs. plain copy vs. plain ref.

In the simple-minded model I discussed, the above would be

  stream out(127.0.0.1:6001");
  // ...
  for_each(v.begin(), v.end(),
           (stream& s) ((int x) { s.send(x); }) (out));

-- Gaby


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk