¿Se garantiza que el lambda sin captura estará vacío por el estándar?

12

Estoy buscando una manera de identificar lambdas vacías (sin captura) de otras lambdas en una función de plantilla. Actualmente estoy usando C ++ 17 pero tengo curiosidad por las respuestas de C ++ 20 también.

Mi código se ve así:

template<typename T>
auto func(T lambda) {
    // The aguments of the lambdas are unknown

    if constexpr (/* is captureless */) {
        // do stuff
    }
}

¿Está garantizado por el estándar C ++ (17 o 20) que una lambda sin captura, que es convertible en un puntero de función, también hará que el std::is_emptyrendimiento sea verdadero?

Tome este código como ejemplo:

auto a = []{}; // captureless
auto b = [c = 'z']{}; // has captures

static_assert(sizeof(a) == sizeof(b)); // Both are the same size
static_assert(!std::is_empty_v<decltype(b)>); // It has a `c` member
static_assert(std::is_empty_v<decltype(a)>); // Passes. It is guaranteed?

Ejemplo en vivo

Racicot Guillaume
fuente
2
Si solo le interesan las lambdas que no sean plantillas, puede usar SFINAE para verificar si la conversión a un puntero de función ( +lambda) está bien formada.
HolyBlackCat
@HolyBlackCat Pensé en eso, pero por lo que recuerdo, MSVC no lo permite ya que sobrecargó el operador de conversión.
Guillaume Racicot
@GuillaumeRacicot MS expone un operador de conversión separado para todas las convenciones de llamadas disponibles. Simplemente elija uno e intente convertir el lambda a un puntero de función comparable, y verifique si eso tiene éxito o falla.
Remy Lebeau
+Parece funcionar aquí .
HolyBlackCat

Respuestas:

13

No, de hecho, el estándar otorga explícitamente permiso para que las lambdas tengan un tamaño que no se alinea con su declaración. [expr.prim.lambda.closure] / 2 estados

El tipo de cierre se declara en el ámbito de bloque más pequeño, ámbito de clase o ámbito de espacio de nombres que contiene la expresión lambda correspondiente. [Nota: Esto determina el conjunto de espacios de nombres y clases asociados con el tipo de cierre ([basic.lookup.argdep]). Los tipos de parámetros de un declarador lambda no afectan estos espacios de nombres y clases asociados. - nota final] El tipo de cierre no es un tipo agregado. Una implementación puede definir el tipo de cierre de manera diferente a lo que se describe a continuación, siempre que esto no altere el comportamiento observable del programa que no sea cambiando:

  • el tamaño y / o alineación del tipo de cierre,

  • si el tipo de cierre se puede copiar trivialmente ([class.prop]) o (2.3)

  • si el tipo de cierre es una clase de diseño estándar ([class.prop]).

Una implementación no agregará miembros del tipo de referencia rvalue al tipo de cierre.

énfasis mío

Por lo tanto, esto permite que la implementación le otorgue a lambda un miembro incluso si no tiene captura. No creo que alguna implementación lo haga, pero están legalmente autorizados para hacerlo.

NathanOliver
fuente