![]() |
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 |
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.