Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C++ (http://www.velocityreviews.com/forums/f39-c.html)
-   -   Re: Lambda capture rules vs. const int values (http://www.velocityreviews.com/forums/t954593-re-lambda-capture-rules-vs-const-int-values.html)

SG 11-18-2012 09:20 AM

Re: Lambda capture rules vs. const int values
 
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


mmehlich 11-20-2012 10:26 PM

Re: Lambda capture rules vs. const int values
 
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 <sgesem...@gmail.invalid> 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 -




All times are GMT. The time now is 02:24 AM.

Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57