Dependent expression and ODR usage in common Lambda in C ++ 14

void f( int , const int (&)[2] = {}) { } // #1 void f( int , const int (&)[1] ) { } // #2 // void f(const int&, const int (&)[1] ) { } // #2_original void test() { const int x = 17; auto g = [](auto a) { f(x); // OK: calls #1, does not capture x }; auto g2 = [ /* = */ ](auto a) { int selector[ sizeof(a) == 1 ? 1 : 2 ]{}; f(x, selector); // OK: is a dependent expression, so captures x ??? }; } 

This is an example from the C ++ 14 standard (ISO / IEC 14882: 2014), section 5.1.2, paragraph 12, which I changed in two ways:

  • Firstly, both versions of the f() function have int as their first argument, so the variable x is not used odr in any case.
  • Secondly, I deleted (commented out) the default capture value in lambda g2 .

Is this code standard? Both clang and gcc compile successfully. However, in the original example, lambda g2 had the default capture value ( [=] ), so the variable x was implicitly captured because there was a dependent expression (and also because it could be used by odr in the f() #2_original ). Note that in the above paragraph of the Standard there are 2 conditions for implicit capture of the variable x (ORR dependent expression). Now, without grabbing by default or using odr:

  • Is this not a compile-time error, since there is a dependent expression and there is no default value for capture? That is, the variable x must be captured, but it cannot be (suppose g2 calls with both types of arguments, i.e., with obtaining sizeof(a)=1 and other sizeof(a)>1 ).

  • Or does the condition of the dependent expression for implicit capture of a variable apply only when there is a default capture? This would mean that without using odr (i.e., without const int& in the function f() #2 ), the program would work the same regardless of the default capture value. Therefore, would the second condition regarding the dependent expression be useless?

This is C ++ 14 standard (ISO / IEC 14882: 2014), section 5.1.2, paragraph 12 (emphasis mine):

A default - bound lambda expression that does not explicitly commit this or a variable with automatic storage duration (this excludes any identification expression that has been found to refer to init captures a related non-static data element), it is said to implicitly capture an object (i.e., this or a variable) if the compound operator:

  • odr-uses (3.2) an object, or
  • names the object in the potentially computed expression (3.2), where the encompassing full expression depends on the general parameter of the lambda declared within the scope of the lambda expression.

Note: lambda g does not capture the variable x because it is not used in f(x) (see C ++ 14 Standard (ISO / IEC 14882: 2014), section 5.1.2, paragraph 13)

References:

+6
source share
1 answer

Is this code standard?

Yes. Rule:

If the lambda expression or instance of the operator template for calling the function of general lambda-odr-use (3.2) this or a variable with the duration of automatic storage from its scope, this object should be captured by the lambda expression.

Is lambda odr-use x ? No, due to [basic.def.odr]:

A variable x whose name appears as a potentially evaluated expression ex is odr-used by ex if applying the lvalue-to-rvalue transformation (4.1) to x does not give a constant expression (5.20) that does not call any non-trivial functions, and if x is object, ex is an element of the set of potential results of expression e , where either the lvalue-to-r transformation (4.1) is applied to e , or e is an expression with a discarded value (paragraph 5).

x is an integral constant that is used in an expression that uses the lvalue-rvalue transformation, so it is not used by odr. Since it is not used by odr, this is not an error that you did not capture.

It would be badly formed if there was an overload f that took its first argument by reference - that the call operator instance would use odr-use x , but it was not captured, which made it poorly formed.


The section you are quoting is not relevant to your modified example. It only refers to a Lambda expression with a default binding . But your lambda has no default hold. The default value for capture is = or & , the intaker [] no default value for capture. However, if we had [=] or [&] , this section would explain why x would be captured.

+2
source

Source: https://habr.com/ru/post/1015443/


All Articles