c++ - examples - lambda como miembro estático
lambda examples c++ (2)
Estoy tratando de usar un lambda como un miembro estático, como este:
struct A
{
static constexpr auto F = [](){};
};
int main()
{
A::F();
return 0;
}
¿Es este el código correcto de C ++ 11? En clang, obtengo este error:
error: constexpr variable ''F'' must be initialized by a constant
expression
static constexpr auto F = [](){};
^~~~~~
Parece en clang, las lambdas no se consideran una expresión constante. ¿Es esto correcto? Quizás todavía no hayan implementado completamente lambdas en clang porque gcc 4.7 parece permitirlo como un 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 lambda, pero solo lo declara y no lo define. ¿Cómo voy a definirlo?
Este código está mal formado. Se constexpr
variable constexpr
sea inicializada mediante una expresión constante, y [expr.const]p2
dice:
Una expresión condicional es una expresión constante central a menos que implique una de las siguientes como una subexpresión potencialmente evaluada [...]:
- una expresión lambda
GCC es por lo tanto incorrecto al aceptar este código.
Esta es una forma 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;
Puedes hacer que funcione, en clang 3.4, siempre que la lambda no capture nada. La idea es 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;
}
Aquí hay dos partes potencialmente polémicas. 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
b ? v1 : v2
puede ser un constexpr
sin requerir que los tres de b
, v1
, v2
sean constexpr
. Basta con que b
sea constexpr
junto con uno de los dos restantes (dependiendo de si b
es true
o false
. Aquí b
es false
, y esto selecciona la parte final de ?:
, Es decir, nullptr
.
En otras palabras, false ? a_non_constexpr_func() : a_constexpr_func()
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 el clang "no debería aceptar esto". Parece ser una relajación válida de la regla. La parte no evaluada de a ?:
está valorada y, por lo tanto, su constexpr
no debería importar.
De todos modos, suponiendo que esto está bien, eso nos da un nullptr
del tipo correcto, es decir, el tipo de un puntero a la lambda. El segundo bit polémico es (*A::F)(1337);
donde estamos desreferenciando el puntero nulo. Pero la Pythy vinculada anteriormente argumenta que eso no es un problema:
Parece que estamos desactivando un puntero nulo. Recuerde que en C ++ al desreferenciar un puntero nulo, se produce un comportamiento indefinido cuando hay una conversión lvalue-r-value. Sin embargo, dado que un cierre lambda no capturable casi siempre se implementa como un objeto sin miembros, nunca ocurre un comportamiento indefinido, ya que no accederá a ninguno de sus miembros. Es altamente improbable que un cierre lambda no capturable pueda implementarse de otra manera, ya que debe ser convertible a un puntero de función. Pero la biblioteca afirma de manera estática que el objeto de cierre está vacío para evitar cualquier posible comportamiento indefinido.