Boost logo

Boost :

From: Gabriel Dos Reis (gdr_at_[hidden])
Date: 2004-03-08 17:45:16


Jaakko Jarvi <jajarvi_at_[hidden]> writes:

| On Mar 8, 2004, at 11:40 AM, Gabriel Dos Reis wrote:
|
| > Jaakko Jarvi <jajarvi_at_[hidden]> writes:
| >
| > | On Sun, 8 Mar 2004, Gabriel Dos Reis wrote:
| > |
| > | > Jaakko Jarvi <jajarvi_at_[hidden]> writes:
| > | >
| > |
| > | 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));
| >
|
|
| Yes, but this is a quite complex and hard to read way to express
| something simpler.

Escaping variables are no simple :-) I doubt there is a simple criteria
that says which ones are "good" and which ones are "bad", and those may
depend on the context, thus let the user labels the good ones :-)

It is possible to have a design that makes copy by default, but it
handles only a very limited set (those for which functions/operations
are expected to take values) at the cost of penalizing the reference
semantics[1]. In a language like C++ where values/references look
like the same syntactically but leak differently, that opens door for
traps more often than needed. Yes, one can advance the argument that
the situation already exists (see recent min/max thread); but do we
need more ropes, especially when we want to talk people into using
the feature? I believe that we should first optimize for the most
simple common cases. Release the product, and extend later.

Lambdas are handy and fun to program with; I'm less convinced that we
would succeed in evangelizing C++ programmers into lambdas in the next
10-15 years, where traditional, well-developed, functional programming
languages have failed for half a decade.

Well, before this message provokes a deluge of mails, I would
probably point out that I'm a big fan of lambdas; but realistically I
don't see C++ brainwashing armies of programmers into lambdas. This
lambda-conversion, if it were to happen, need not be done in a one
single big-step. Just think about the perception of lambdas before
the STL and after, say, 1998. If you optimize for the most common
simple cases, then people will use it, get adicted and want more.
That is essentially what STL did.

But, I see we are in violent agreement on one fundamental point:
lambdas in C++ should be bound to lexical scopes (e.g. implemented by
local classes). That provides opportunities for efficiency and they can
be effectively optimized (with the hope that compilers understand the
semantics of scopes, which some of them do). If you let the
possibility to escape by default, compilers would have more difficulty
optimizing and would provide less effective support. This has been
done before. The experience was not that conclusive[2].

| And it seems that in the inner lambda, x would be a variable from an
| outer scope
| with automatic storage, just like i is, and would therefore have to be
| rejected
| by the current local class rules.

Hmm, I don't get that one. "x" in the inner lambda is the "x" bound
by the outer (auto x). Therefore it is not rejected.
Or am I being dense?

Footnotes:
[1] Simple constructs like

    struct foo {
       int bar() { return 666; }
    };

    template<class T>
       int apply(foo* p, T f)
       { return (p->*f)(); }

     int main()
     {
        foo t[5];
         for (int i = 0; i < 5; ++i)
           apply(t + i, &foo::bar);
     }

    are not effectively optimized by (common) compilers (especially,
    indirect calls through pointer to members or pointer to functions).
    Yes, in the abstract they can (and should) do better. But, in
    practice, few do correctly.

[2] Lisp.

-- Gaby


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