I've encountered an interesting case (at least for me) when using lambdas and was wondering whether it is a compiler bug or something allowed by the standard feature.
Let's cut to the chase. Having sample code:
const int controlValue = 5;
std::vector<int> vect{ 0, 1, 2, 3 };
const auto result = std::any_of(vect.begin(), vect.end(), [](const int& item)
{
return item == controlValue;
});
Notice that controlValue
variable is not captured by the lambda expression.
Additionally, in the cppreference for lambda expressions it is stated that [] - captures nothing
Using VS2015 for compilation of the above code gives an error which is not surprising:
error C3493: 'controlValue' cannot be implicitly captured because no default capture mode has been specified
However, when using MinGW with gcc 4.8.2 same example compiles and works. Some online compilers including gcc 5.4.0, clang 3.8.0 give similar result.
When controlValue
loses its const
then all tested compilers give the error all expect (that the variable is not captured which is fine).
Which of the compilers is standard compliant in this case? Does this mean that some optimizations or other "hacks" are used for const variables here? Maybe something is captured implicitly? Could anyone explain the situation happening here?
EDIT:
Some pointed out that this question is a duplicate of Lambda capturing constexpr object . While the answer there may be somewhat related (points to the odr-use case) the question there is about an error occurring while capturing by ref. The topic here is quite different and focuses on not capturing explicitly a variable at all (although using it in the lambda body).
After looking through more lambda related questions, if someone's interested, I'd point to Using lambda captured constexpr value as an array dimension
which (same as @Barry stated) suggests VS2015 bug and shows that setting the controlValue
variable in the example here to static
fixes the compilation under VS2015.
Much like functions can change the value of arguments passed by reference, we can also capture variables by reference to allow our lambda to affect the value of the argument. To capture a variable by reference, we prepend an ampersand ( & ) to the variable name in the capture.
A lambda expression can refer to identifiers declared outside the lambda expression. If the identifier is a local variable or a reference with automatic storage duration, it is an up-level reference and must be "captured" by the lambda expression.
Throwing an exception from a lambda function is equivalent like throwing an exception from any other function.
Explanation: There are three ways in which we can capture the external variables inside the lambda expression namely capture by reference, capture by value and capture by both that is mixed capture.
This is a VS bug. The code is perfectly well-formed.
The rule in [expr.prim.lambda] is:
If a lambda-expression or an instantiation of the function call operator template of a generic lambda odr-uses (3.2) this or a variable with automatic storage duration from its reaching scope, that entity shall be captured by the lambda-expression.
Where a variable is odr-used if, according to [basic.def.odr]:
A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.20) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression (Clause 5).
And, from [expr.const]:
A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions: [...] an lvalue-to-rvalue conversion (4.1) unless it is applied to a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression
In:
return item == controlValue;
controlValue
is a glvalue of integral type that refers to a complete non-volatile const object initialized with a constant expression. Hence, when we use controlValue
in a context that involves an lvalue-to-rvalue conversion, it is not odr-used. Since it's not odr-used, we don't need to capture it.
When you changed controlValue
to be non-const
, it's ceases to be a constant expression, and the equality check odr-uses it. Since it's not captured but is odr-used, the lambda is ill-formed.
Note that exactly such an example appears in the standard:
void f(int, const int (&)[2] = {}) { } // #1
void f(const int&, const int (&)[1]) { } // #2
void test() {
const int x = 17;
auto g = [](auto a) {
f(x); // OK: calls #1, does not capture x
};
// ...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With