lambda como miembro estático


Estoy tratando de usar un lambda como un miembro estático, así:

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


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

¿Es este código C++11 correcto? En clang, obtengo este error:

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

Parece que en clang, las lambdas no se consideran una expresión constante. ¿Es correcto? Quizás aún no han implementado lambdas completamente en clang porque gcc 4.7 parece permitirlo como constexpr, pero da otro error:

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

No estoy seguro, entiendo lo que eso significa. Parece deducir correctamente el tipo de la lambda, pero solo lo declara y no lo define. ¿Cómo lo definiría?

Author: Mark Hurd, 2012-07-30

2 answers

Este código está mal formado. Se requiere que una variable constexpr sea inicializada por una expresión constante, y [expr.const]p2 dice:

Una expresión condicional es una expresión constante central a menos que involucre una de las siguientes como una subexpresión potencialmente evaluada [...]:

  • a lambda-expresión

Por lo tanto, GCC es incorrecta al aceptar este código.

Aquí hay una manera de dar a una clase un miembro de datos estáticos de tipo lambda:

auto a = []{};
struct S {
  static decltype(a) b;
};
decltype(a) S::b = a;
 17
Author: Richard Smith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-03 21:30:20

Puede hacer que funcione, en clang 3.4, siempre y cuando la lambda no capture nada. La idea proviene directamente de 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;
}

Hay dos partes potencialmente controvertidas aquí. Primero, está el hecho de que A::F es constexpr, pero tiene una lambda en su definición.

Eso debería ser imposible ¿verdad? No. Una expresión ternaria b ? v1 : v2 puede ser una constexpr sin requerir los tres b, v1, v2 ser constexpr. Es suficiente simplemente que b es constexpr a lo largo con una de los dos restantes (dependiendo de si b es true o false. Aquí b es false, y esto selecciona la parte final del ?:, es decir, nullptr.

En otras palabras false ? a_non_constexpr_func() : a_constexpr_func() es un constexpr. Esta parece ser la interpretación en clang de todos modos. Espero que esto sea lo que está en el estándar. Si no, no diría que clang "no debería aceptar esto". Parece ser una relajación válida de la regla. La parte no evaluada de un ?: no es evaluada y por lo tanto es constexpr -ess no debería importar.

De todos modos, suponiendo que esto está bien, nos da un nullptr del tipo correcto, es decir, el tipo de un puntero a la lambda. El segundo bit controvertido es (*A::F)(1337); donde estamos desreferenciando el puntero nulo. Pero es argumentado por la página enlazada arriba que eso no es un problema:

Parece que estamos desrefenciando un puntero nulo. Recuerde que en C++ al desreferenciar un puntero nulo, el comportamiento indefinido ocurre cuando hay un lvalue-to-rvalue conversion. Sin embargo, dado que un cierre lambda sin captura casi siempre se implementa como un objeto sin miembros, nunca se produce un comportamiento indefinido, ya que no accederá a ninguno de sus miembros. Es muy poco probable que un cierre lambda sin captura pueda implementarse de otra manera, ya que debe ser convertible a un puntero de función. Pero la biblioteca afirma estáticamente que el objeto closure está vacío para evitar cualquier posible comportamiento indefinido.

 2
Author: Aaron McDaid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-27 18:00:37