lambda作为静态成员

时间:2012-07-30 16:43:56

标签: c++ lambda c++11 language-lawyer constexpr

我正在尝试使用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的类型,但它只声明它而不是定义它。我将如何定义它?

2 个答案:

答案 0 :(得分:17)

此代码格式错误。 constexpr变量需要通过常量表达式初始化,[expr.const]p2表示:

  

条件表达式是一个核心常量表达式,除非它涉及下列其中一项作为潜在评估的子表达式[...]:

     
      
  • a lambda-expression
  •   
因此,GCC接受此代码是不正确的。

这是为类提供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::Fconstexpr,但它的定义中有一个lambda。

这应该是不可能的吗?不可以。三元表达式b ? v1 : v2可以是constexpr,而不要求bv1v2中的所有三个都是constexpr。仅bconstexpr以及其余两个一个就足够了(取决于btrue还是{{1}这里falseb,这会选择false的最后一部分,即?:

换句话说,nullptrfalse ? a_non_constexpr_func() : a_constexpr_func()。无论如何,这似乎是铿锵的解释。我希望这是标准中的内容。如果没有,我不会说铿锵“不应该接受这个”。这似乎是对规则的有效放宽。 constexpr的未评估部分未经评估,因此?: - 无关紧要。

无论如何,假设这是正常的,那就给我们一个正确类型的constexpr,即指向lambda的指针的类型。第二个有争议的位是nullptr,我们在这里取消引用空指针。但是上面提到的page认为这不是一个问题:

  

看来我们正在derefencing一个空指针。记住在C ++中取消引用空指针时,当存在左值到右值的转换时会发生未定义的行为。但是,由于非捕获lambda闭包几乎总是作为没有成员的对象实现,因此不会发生未定义的行为,因为它不会访问其任何成员。由于它必须可以转换为函数指针,所以非常不可能以另一种方式实现非捕获lambda闭包。但是库确实断言闭包对象是空的,以避免任何可能的未定义行为。