我正在尝试使用lambda作为静态成员,如下所示:
struct A
{
static constexpr auto F = [](){};
};
int main()
{
A::F();
return 0;
}
这是否是正确的C ++ 11代码?在clang上,我收到了这个错误:
error: constexpr variable 'F' must be initialized by a constant
expression
static constexpr auto F = [](){};
^~~~~~
似乎在clang中,lambdas不被视为常数表达式。它是否正确?也许他们还没有在clang中完全实现lambdas,因为gcc 4.7似乎允许它作为constexpr
,但它给出了另一个错误:
error: ‘constexpr const<lambda()> A::F’, declared using local type ‘const<lambda()>’, is used but never defined
我不确定,我明白这意味着什么。它似乎正确地推断出lambda的类型,但它只声明它而不是定义它。我将如何定义它?
答案 0 :(得分:17)
此代码格式错误。 constexpr
变量需要通过常量表达式初始化,[expr.const]p2
表示:
因此,GCC接受此代码是不正确的。条件表达式是一个核心常量表达式,除非它涉及下列其中一项作为潜在评估的子表达式[...]:
- a lambda-expression
这是为类提供lambda类型的静态数据成员的一种方法:
auto a = []{};
struct S {
static decltype(a) b;
};
decltype(a) S::b = a;
答案 1 :(得分:2)
只要lambda没有捕获任何内容,你就可以在clang 3.4中使它工作。这个想法直接来自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;
}
这里有两个可能有争议的位。首先,A::F
是constexpr
,但它的定义中有一个lambda。
这应该是不可能的吗?不可以。三元表达式b ? v1 : v2
可以是constexpr
,而不要求b
,v1
,v2
中的所有三个都是constexpr
。仅b
为constexpr
以及其余两个一个就足够了(取决于b
是true
还是{{1}这里false
是b
,这会选择false
的最后一部分,即?:
。
换句话说,nullptr
是false ? a_non_constexpr_func() : a_constexpr_func()
。无论如何,这似乎是铿锵的解释。我希望这是标准中的内容。如果没有,我不会说铿锵“不应该接受这个”。这似乎是对规则的有效放宽。 constexpr
的未评估部分未经评估,因此?:
- 无关紧要。
无论如何,假设这是正常的,那就给我们一个正确类型的constexpr
,即指向lambda的指针的类型。第二个有争议的位是nullptr
,我们在这里取消引用空指针。但是上面提到的page认为这不是一个问题:
看来我们正在derefencing一个空指针。记住在C ++中取消引用空指针时,当存在左值到右值的转换时会发生未定义的行为。但是,由于非捕获lambda闭包几乎总是作为没有成员的对象实现,因此不会发生未定义的行为,因为它不会访问其任何成员。由于它必须可以转换为函数指针,所以非常不可能以另一种方式实现非捕获lambda闭包。但是库确实断言闭包对象是空的,以避免任何可能的未定义行为。