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?
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 [...]:
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.