I'm trying to use a lambda as a static member, like this:
struct A { static constexpr auto F = [](){}; }; int main() { A::F(); return 0; }
Is this even correct C++11 code? On clang, I get this error:
error: constexpr variable 'F' must be initialized by a constant expression static constexpr auto F = [](){}; ^~~~~~
It seems in clang, lambdas aren't considered a constant expression. Is this correct? Perhaps they haven't fully implemented lambdas yet in clang because gcc 4.7 seems to allow it as a constexpr
, but it give another error:
error: ‘constexpr const<lambda()> A::F’, declared using local type ‘const<lambda()>’, is used but never defined
I'm not sure, I understand what that means. It seems to correctly deduce the type of the lambda, but it only declares it and not define it. How would I go about defining it?
You can't declare the lambda expression static.
Visual Studio 2017 version 15.3 and later (available in /std:c++17 mode and later): A lambda expression may be declared as constexpr or used in a constant expression when the initialization of each data member that it captures or introduces is allowed within a constant expression.
Static Function MembersA static member function can only access static data member, other static member functions and any other functions from outside the class. Static member functions have a class scope and they do not have access to the this pointer of the class.
No. Lambdas are ultimately objects that have a function call operator. A captureless lambda can be converted into a function pointer of its appropriate type, but that function pointer will be a C++ function with C++ linkage.
This code is ill-formed. A constexpr
variable is required to be initialized by a constant expression, and [expr.const]p2
says:
A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression [...]:
- a lambda-expression
GCC is therefore incorrect to accept this code.
Here's one way to give a class a static data member of lambda type:
auto a = []{}; struct S { static decltype(a) b; }; decltype(a) S::b = a;
You can make it work, in clang 3.4, as long as the lambda doesn't capture anything. The idea is directly from Pythy .
#include <type_traits> #include <iostream> template<typename T> auto address(T&& t) -> typename std:: remove_reference<T> :: type * { return &t; } struct A { static constexpr auto * F = false ? address( [](int x){ std:: cout << "It worked. x = " << x << std:: endl; } ) : nullptr; // a nullptr, but at least its *type* is useful }; int main() { (*A::F)(1337); // dereferencing a null. Doesn't look good return 0; }
There are two potentially controversial bits here. First, there's the fact that A::F
is constexpr
, but it has a lambda in its definition.
That should be impossible right? No. A ternary expression b ? v1 : v2
can be a constexpr
without requiring all three of b
, v1
, v2
to be constexpr
. It is sufficient merely that b
is constexpr
along with one of the remaining two (depending on whether b
is true
or false
. Here b
is false
, and this selects the final part of the ?:
, i.e. nullptr
.
In other words false ? a_non_constexpr_func() : a_constexpr_func()
is a constexpr
. This appears to be the interpretation in clang anyway. I hope this is what's in the standard. If not, I wouldn't say that clang "should not accept this". It appears to be a valid relaxation of the rule. The unevaluated part of a ?:
is unevaluated and therefore it constexpr
-ness shouldn't matter.
Anyway, assuming this is OK, that gives us a nullptr
of the correct type, i.e. the type of a pointer to the lambda. The second controversial bit is (*A::F)(1337);
where we are dereferencing the null pointer. But it is argued by the page linked above that that is not a problem:
It appears that we are derefencing a null pointer. Remember in C++ when dereferencing a null pointer, undefined behavior occurs when there is an lvalue-to-rvalue conversion. However, since a non-capturing lambda closure is almost always implemented as an object with no members, undefined behavior never occurs, since it won't access any of its members. Its highly unlikely that a non-capturing lambda closure could be implemented another way since it must be convertible to a function pointer. But the library does statically assert that the closure object is empty to avoid any possible undefined behavior.
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