Re: Lambda capture rules vs. const int values

Discussion in 'C++' started by SG, Nov 18, 2012.

  1. SG

    SG Guest

    Am 18.11.2012 02:49, schrieb Michael Mehlich:
    > /*
    > Lambda capture rules vs. const int values
    >
    > I'm trying to understand the capture rules for lambdas in C++11, in
    > particular related to capturing const integral values.
    > This has resulted in the question whether the uses of x in the lambdas
    > in the example below are considered odr-uses or not.
    > Some experimentation with clang 3.1 and gcc 4.7.2 suggest that compilers
    > do not agree on this.
    >
    > What does the standard say about the behavior of the following code:
    > */
    >
    > extern "C" int printf(const char *, ...);
    >
    > void foo(int &&) { printf("void foo(int &&)\n"); }
    > void foo(const int &) { printf("void foo(const int &)\n"); }
    > void bar(int &&) { printf("void bar(int &&)\n"); }
    > void baz(int) { printf("void baz(int)\n"); }
    > void fbz(const int &) { printf("void fbz(const int &)\n"); }
    >
    > int main() {
    > const int x = 1;
    > auto lf = [=] { foo(x); };
    > auto lb = [=] { bar(x); };
    > // clang: no matching function for call to bar
    > auto lz = [=] { baz(x); };
    > auto lm = [=] { fbz(x); };
    > lf(); // clang prints "void foo(const int &)"
    > // gcc prints "void foo(int &&)"
    > lb(); // clang N/A
    > // gcc prints "void bar(int &&)"
    > lz(); // clang prints "void baz(int)"
    > // gcc prints "void baz(int)"
    > lm(); // clang prints "void fbz(const int &)"
    > // gcc prints "void fbz(const int &)"
    > printf("%d\n",sizeof(lf));
    > // clang prints "4" (captures x)
    > // gcc prints "1" (does not capture x)
    > printf("%d\n",sizeof(lb));
    > // clang N/A
    > // gcc prints "1" (does not capture x)
    > printf("%d\n",sizeof(lz));
    > // clang prints "1" (does not capture x)
    > // gcc prints "1" (does not capture x)
    > printf("%d\n",sizeof(lm))
    > // clang prints "4" (captures x)
    > // gcc prints "1" (does not capture x)
    > }


    Interesting!

    It seems like GCC is a little more eager in optimizing the constant away
    and inserts a prvalue in-place of x within the lambda. However, by doing
    that, it obviously changes the observable behaviour. Suddently x is not
    an lvalue anymore but a prvalue. I don't think that this is a conforming
    behaviour because it obviously affects overload resolution and one can
    see address of the temporary int object. In the case of "baz(int)",
    clang does the same but this optimization is not observable apart from
    the size of the lambda which can legally be anything. If I remember
    correctly, the standard specifically includes a section telling us that
    certain properties of the closure types and objects may vary and I
    believe its size is one of them.

    The closure type is expected to be similar to the following type with
    respect to the treatment of x:

    class closure_type
    {
    const int x;
    public:
    :::
    void operator()() const
    {
    :::
    }
    :::
    };

    So, my conclusion would be: (1) GCC is over-eager in optimizing this
    constant away in a non-conforming way, and (2) clang is right.

    Cheers!
    SG
     
    SG, Nov 18, 2012
    #1
    1. Advertising

  2. SG

    mmehlich Guest

    Technically, the standard says that only odr-used variables
    are implicitly captured, and that referencing a const int x as
    specified below does not constitute an odr-use if and only if an
    lvalue-to-rvalue transformation is immediately applied to it, so
    in the case of calling baz(int), x is not being captured. Not
    capturing x in that case therefore is not an optimization, but
    required by the standard (notwithstanding the fact that an
    implementation is allowed to make room for x in the capture class
    and may even actively copy the local x into the instance of the
    capture class).

    Making the odr-use judgement *after* overload resolution definitely
    makes more sense than before (and makes the behavior more consistent
    with the same situation outside of lambda bodies), so I would expect
    the clang behavior to be the intended one; but then, I have been
    surprised by the impact of capture rules several times already.
    (E.g., sizeof applied to an expression does not necessarily provide
    the size of the result type of the expression when the expression is
    used in an evaluated context.)

    --
    Michael

    On Nov 18, 4:20 am, SG <> wrote:
    > Am 18.11.2012 02:49, schrieb Michael Mehlich:
    >
    >
    >
    >
    >
    > > /*
    > > Lambda capture rules vs. const int values

    >
    > > I'm trying to understand the capture rules for lambdas in C++11, in
    > > particular related to capturing const integral values.
    > > This has resulted in the question whether the uses of x in the lambdas
    > > in the example below are considered odr-uses or not.
    > > Some experimentation with clang 3.1 and gcc 4.7.2 suggest that compilers
    > > do not agree on this.

    >
    > > What does the standard say about the behavior of the following code:
    > > */

    >
    > > extern "C" int printf(const char *, ...);

    >
    > > void foo(int &&) { printf("void foo(int &&)\n"); }
    > > void foo(const int &) { printf("void foo(const int &)\n"); }
    > > void bar(int &&) { printf("void bar(int &&)\n"); }
    > > void baz(int) { printf("void baz(int)\n"); }
    > > void fbz(const int &) { printf("void fbz(const int &)\n"); }

    >
    > > int main() {
    > >     const int x = 1;
    > >     auto lf = [=] { foo(x); };
    > >     auto lb = [=] { bar(x); };
    > >                // clang: no matching function for call to bar
    > >     auto lz = [=] { baz(x); };
    > >     auto lm = [=] { fbz(x); };
    > >     lf(); // clang prints "void foo(const int &)"
    > >                // gcc prints "void foo(int &&)"
    > >     lb(); // clang N/A
    > >                // gcc prints "void bar(int &&)"
    > >     lz(); // clang prints "void baz(int)"
    > >                // gcc prints "void baz(int)"
    > >     lm(); // clang prints "void fbz(const int &)"
    > >                // gcc prints "void fbz(const int &)"
    > >     printf("%d\n",sizeof(lf));
    > >                // clang prints "4" (captures x)
    > >                // gcc prints "1" (does not capture x)
    > >     printf("%d\n",sizeof(lb));
    > >                // clang N/A
    > >                // gcc prints "1" (does not capture x)
    > >     printf("%d\n",sizeof(lz));
    > >                // clang prints "1" (does not capture x)
    > >                // gcc prints "1" (does not capture x)
    > >     printf("%d\n",sizeof(lm))
    > >                // clang prints "4" (captures x)
    > >                // gcc prints "1" (does not capture x)
    > > }

    >
    > Interesting!
    >
    > It seems like GCC is a little more eager in optimizing the constant away
    > and inserts a prvalue in-place of x within the lambda. However, by doing
    > that, it obviously changes the observable behaviour. Suddently x is not
    > an lvalue anymore but a prvalue. I don't think that this is a conforming
    > behaviour because it obviously affects overload resolution and one can
    > see address of the temporary int object. In the case of "baz(int)",
    > clang does the same but this optimization is not observable apart from
    > the size of the lambda which can legally be anything. If I remember
    > correctly, the standard specifically includes a section telling us that
    > certain properties of the closure types and objects may vary and I
    > believe its size is one of them.
    >
    > The closure type is expected to be similar to the following type with
    > respect to the treatment of x:
    >
    >     class closure_type
    >     {
    >         const int x;
    >     public:
    >         :::
    >         void operator()() const
    >         {
    >             :::
    >         }
    >         :::
    >     };
    >
    > So, my conclusion would be: (1) GCC is over-eager in optimizing this
    > constant away in a non-conforming way, and (2) clang is right.
    >
    > Cheers!
    > SG- Hide quoted text -
    >
    > - Show quoted text -
     
    mmehlich, Nov 20, 2012
    #2
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Timo Freiberger
    Replies:
    3
    Views:
    998
    Bob Hairgrove
    Oct 30, 2004
  2. ThazKool
    Replies:
    1
    Views:
    463
  3. Replies:
    11
    Views:
    1,153
  4. Javier
    Replies:
    2
    Views:
    627
    James Kanze
    Sep 4, 2007
  5. 0m
    Replies:
    26
    Views:
    1,174
    Tim Rentsch
    Nov 10, 2008
Loading...

Share This Page