28

我正在尝试使用 lambda 作为静态成员,如下所示:

struct A
{
    static constexpr auto F = [](){};
};


int main()
{
    A::F();
    return 0;
}

这甚至是正确的 C++11 代码吗?在铿锵声中,我收到此错误:

error: constexpr variable 'F' must be initialized by a constant
      expression
    static constexpr auto F = [](){};
                              ^~~~~~

在 clang 中,lambda 似乎不被视为常量表达式。它是否正确?也许他们还没有在 clang 中完全实现 lambdas,因为 gcc 4.7 似乎允许它作为 a constexpr,但它给出了另一个错误:

error: ‘constexpr const<lambda()> A::F’, declared using local type ‘const<lambda()>’, is used but never defined

我不确定,我明白这意味着什么。它似乎正确地推断出 lambda 的类型,但它只声明它而不定义它。我将如何定义它?

4

2 回答 2

17

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

条件表达式核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式 [...]:

  • 一个lambda 表达式

因此,GCC 接受此代码是不正确的。

这是给类一个 lambda 类型的静态数据成员的一种方法:

auto a = []{};
struct S {
  static decltype(a) b;
};
decltype(a) S::b = a;
于 2012-07-31T06:06:17.277 回答
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可以是 aconstexpr而不需要所有三个b, v1,v2都是constexpr。仅与其余两个之一一起就足够bconstexpr取决于是还是。这里是,这将选择 的最后部分,即。btruefalsebfalse?:nullptr

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

无论如何,假设这没问题,这给了我们nullptr正确类型的 a,即指向 lambda 的指针的类型。第二个有争议的(*A::F)(1337);地方是我们取消引用空指针的地方。但是上面链接的页面认为这不是问题:

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

于 2014-02-27T18:00:37.090 回答