I have been attempting to understand when and when not a lambda with a capture-default odr-uses a variable with automatic storage duration defined in its surrounding scope (prompted by this answer). While exploring around this I came across a little curiosity. GCC and Clang appear to disagree about the value category of the id-expression n
in the following code:
template <typename T> void assert_is_lvalue(const T&) {}
template <typename T> void assert_is_lvalue(const T&&) = delete;
int main() {
const int n = 0;
[=] { assert_is_lvalue(n); };
}
Clang compiles the code successfully, while GCC does not (error: use of deleted function
). Which one is correct? Or is this something that is unspecified or implementation-defined?
Binding a reference to an object is supposed to odr-use it, and this is confirmed by removing the lambda's capture-default and observing that both compilers then complain that n
can not be implicitly captured without a capture-default.
Marking the lambda as mutable
makes no appreciable difference to the compilers' output.
By default, variables are captured by const value . This means when the lambda is created, the lambda captures a constant copy of the outer scope variable, which means that the lambda is not allowed to modify them.
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.
The capture list defines the outside variables that are accessible from within the lambda function body. The only capture defaults are. & (implicitly capture the used automatic variables by reference) and. = (implicitly capture the used automatic variables by copy).
Lambda expression in java Using a local variable is as stated. However, when a lambda expression uses a local variable from its enclosing scope, a special situation is created that is referred to as a variable capture. In this case, a lambda expression may only use local variables that are effectively final.
It turns out gcc behavior has changed from gcc-7.5 upward! I used the following code to see how n is captured within lambda and which template is matched.
#include <iostream>
#include <string>
#include <typeinfo>
#include <type_traits>
#include <memory>
#include <string>
#include <cstdlib>
template <class T>
constexpr std::string_view type_name()
{
using namespace std;
#ifdef __clang__
string_view p = __PRETTY_FUNCTION__;
return string_view(p.data() + 34, p.size() - 34 - 1);
#elif defined(__GNUC__)
string_view p = __PRETTY_FUNCTION__;
#if __cplusplus < 201402
return string_view(p.data() + 36, p.size() - 36 - 1);
#else
return string_view(p.data() + 49, p.find(';', 49) - 49);
#endif
#elif defined(_MSC_VER)
string_view p = __FUNCSIG__;
return string_view(p.data() + 84, p.size() - 84 - 7);
#endif
}
template <typename T>
void assert_is_lvalue(const T& param)
{
std::cout << " T is " << type_name<T>()
<< " param is " << type_name<decltype(param)>() << '\n';
}
//template <typename T> void assert_is_lvalue(const T&&) = delete;
template <typename T>
void assert_is_lvalue(const T&& param)
{
std::cout << " T is " << type_name<T>()
<< " param is " << type_name<decltype(param)>() << '\n';
}
int main()
{
const int n = 0;
[=] {
std::cout << " n is " << type_name<decltype(n)>() << '\n';
assert_is_lvalue(n);
}();
return 0;
}
and here are the results:
gcc-7.5
n is const int
T is int param is const int&&
gcc-8.1
n is const int
T is int param is const int&
you can play with the code here.
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