Подтвердить что ты не робот

Лямбда как статический член

Я пытаюсь использовать лямбда как статический член, например:

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


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

Это даже правильный код С++ 11? На clang, я получаю эту ошибку:

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

Кажется, что в clang lambdas не считаются постоянным выражением. Это верно? Возможно, они еще не полностью реализовали lambdas в clang, потому что gcc 4.7, по-видимому, разрешает его как constexpr, но он дает еще одну ошибку:

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

Я не уверен, я понимаю, что это значит. Кажется, правильно вывести тип лямбды, но он только объявляет его и не определяет. Как я могу определить его?

4b9b3361

Ответ 1

Этот код плохо сформирован. A constexpr требуется инициализировать константное выражение, а [expr.const]p2 говорит:

Условное выражение является выражением основной константы, если оно не связано с одним из следующих как потенциально оцененное подвыражение [...]:

  • лямбда-выражение

Поэтому GCC неправильно принимает этот код.

Здесь один из способов дать классу статический элемент данных лямбда-типа:

auto a = []{};
struct S {
  static decltype(a) b;
};
decltype(a) S::b = a;

Ответ 2

Вы можете заставить его работать, в 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, но в его определении есть лямбда.

Это должно быть невозможно? Нет. Тройное выражение b ? v1 : v2 может быть constexpr, не требуя, чтобы все три из b, v1, v2 были constexpr. Достаточно просто, чтобы b был constexpr вместе с одним из оставшихся двух (в зависимости от того, является ли b true или false. Здесь b есть false, и это выбирает заключительную часть ?:, т.е. nullptr.

Другими словами, false ? a_non_constexpr_func() : a_constexpr_func() есть constexpr. По-видимому, это интерпретация в clang. Надеюсь, это то, что в стандарте. Если нет, я бы не сказал, что clang "не должен принимать это". Это, по-видимому, действительное ослабление правила. Неоценимая часть a ?: не оценена, и поэтому ее значение constexpr не имеет значения.

В любом случае, считая это ОК, это дает нам nullptr правильного типа, т.е. тип указателя на лямбда. Второй противоречивый бит (*A::F)(1337);, где мы разыскиваем нулевой указатель. Но на странице указано, что это не проблема:

Похоже, что мы дефрагментируем нулевой указатель. Помните, что в С++ при разыменовании нулевого указателя поведение undefined происходит при преобразовании lvalue-to-rvalue. Однако, поскольку незахватывающее лямбда-закрытие почти всегда реализуется как объект без членов, поведение undefined никогда не происходит, поскольку оно не будет иметь доступа к любому из его членов. Его очень маловероятно, что не захватывающее лямбда-замыкание может быть реализовано иначе, поскольку оно должно быть преобразовано в указатель функции. Но библиотека статически утверждает, что объект закрытия пуст, чтобы избежать возможного поведения undefined.