¿Se pueden modelar las funciones lambda?

230

En C ++ 11, ¿hay alguna forma de modelar una función lambda? ¿O es inherentemente demasiado específico para ser tentado?

Entiendo que en su lugar puedo definir una clase / functor clásico con plantilla, pero la pregunta es más como: ¿permite el lenguaje crear plantillas de funciones lambda?

Klaim
fuente
¿Hay un caso de uso en el que una plantilla lambda sería útil?
James McNellis
77
James: Podría construir una función para iterar sobre una tupla (no necesariamente útil).
Joe D
Pensé en la idea mientras leía una entrevista de Stroustrup hablando de que la complejidad de la meta-plantilla es un problema. Si se le permitía, me estaba imaginando el ninja código-fu que podrían ser inventado por los programadores demasiado inteligente jugando con esta combinación ofrece ...
Klaim

Respuestas:

181

ACTUALIZACIÓN 2018: C ++ 20 vendrá con lambdas con plantillas y conceptualizadas. La característica ya se ha integrado en el borrador estándar.


ACTUALIZACIÓN 2014: C ++ 14 se lanzó este año y ahora proporciona lambdas polimórficas con la misma sintaxis que en este ejemplo. Algunos compiladores importantes ya lo implementan.


En su posición (en C ++ 11), lamentablemente no. Las lambdas polimórficas serían excelentes en términos de flexibilidad y potencia.

La razón original por la que terminaron siendo monomórficos fue por conceptos. Los conceptos dificultaban esta situación de código:

template <Constraint T>
void foo(T x)
{
    auto bar = [](auto x){}; // imaginary syntax
}

En una plantilla restringida solo puede llamar a otras plantillas restringidas. (De lo contrario, las restricciones no podrían verificarse). ¿Se puede fooinvocar bar(x)? ¿Qué restricciones tiene la lambda (el parámetro para ello es solo una plantilla, después de todo)?

Los conceptos no estaban listos para abordar este tipo de cosas; requeriría más cosas como late_check(donde el concepto no se verificó hasta que se invocó) y otras cosas. Más simple era simplemente dejarlo todo y apegarse a las lambdas monomórficas.

Sin embargo, con la eliminación de conceptos de C ++ 0x, las lambdas polimórficas vuelven a ser una simple proposición. Sin embargo, no puedo encontrar ninguna propuesta para ello. :(

GManNickG
fuente
55
Simple ... excepto que existe el deseo de reintroducir conceptos y evitar características que los complican.
66
Creo que prefiero tener lambdas polimórficas que conceptos. No entiendo cómo el ejemplo motiva algo; simplemente podría prohibirlo como un error y exigir que la lambda sea monomórfica [] (T x) {} o una plantilla restringida [] plantilla <Restricción T> (T x) {}, que puede verificarse estáticamente para que coincida. ¿Hay alguna razón por la cual esto no fue posible?
DrPizza
13
No tiene que elegir entre conceptos y lambdas polimórficos: cpp-next.com/archive/2011/12/a-breakthrough-for-concepts
Dave Abrahams
3
Aquí está la propuesta de lambdas polimórficas: open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf y la implementación del juguete en clang: faisalv.github.com/clang-glambda
Radif Sharafullin
18
Las Lambdas polimórficas estarán en C ++ 14, al menos ahora están en el Borrador de la comunidad :)
Arne Mertz
37

Las lambdas de C ++ 11 no se pueden modelar como se indica en otras respuestas, pero decltype()parece ayudar cuando se usa una lambda dentro de una clase o función con plantilla.

#include <iostream>
#include <string>

using namespace std;

template<typename T>
void boring_template_fn(T t){
    auto identity = [](decltype(t) t){ return t;};
    std::cout << identity(t) << std::endl;
}

int main(int argc, char *argv[]) {
    std::string s("My string");
    boring_template_fn(s);
    boring_template_fn(1024);
    boring_template_fn(true);
}

Huellas dactilares:

My string
1024
1

He descubierto que esta técnica es útil cuando se trabaja con código con plantilla, pero me doy cuenta de que todavía significa que las lambdas mismas no pueden ser creadas con plantillas.

Joel
fuente
26
Tfuncionaría bien en lugar de decltype(t)en este ejemplo.
user2023370
26

En C ++ 11, las funciones lambda no pueden crearse plantillas, pero en la próxima versión del estándar ISO C ++ (a menudo llamado C ++ 14), se introducirá esta característica. [Fuente]

Ejemplo de uso:

auto get_container_size = [] (auto container) { return container.size(); };

Tenga en cuenta que aunque la sintaxis usa la palabra clave auto, la deducción de tipo no usará las reglas de autodeducción de tipo, sino que usará las reglas de deducción de argumentos de plantilla. Consulte también la propuesta de expresiones lambda genéricas (y la actualización de esto).

Timo Türschmann
fuente
55
Las reglas de autodeducción de tipos se definen específicamente para ser las mismas que las de la templatededucción de argumentos de función.
underscore_d
10

Soy consciente de que esta pregunta es sobre C ++ 11. Sin embargo, para aquellos que buscaron en Google y aterrizaron en esta página, las lambdas con plantillas ahora son compatibles con C ++ 14 y reciben el nombre de Lambdas genéricas.

[info] La mayoría de los compiladores populares soportan esta característica ahora. Microsoft Visual Studio 2015 es compatible. Clang apoya. GCC apoya.

RAM
fuente
6

Me pregunto qué pasa con esto:

template <class something>
inline std::function<void()> templateLamda() {
  return [](){ std::cout << something.memberfunc() };
}

Utilicé un código similar como este, para generar una plantilla y me pregunto si el compilador optimizará la función de "ajuste".

ted
fuente
2
Que compilador ¿Lo hizo?
NicoBerrogorry
3

Hay una extensión gcc que permite plantillas lambda :

// create the widgets and set the label
base::for_each(_widgets, [] <typename Key_T, typename Widget_T>
                         (boost::fusion::pair<Key_T, Widget_T*>& pair) -> void {
                             pair.second = new Widget_T();
                             pair.second->set_label_str(Key_T::label);
                          }
              );

donde _widgetses unstd::tuple< fusion::pair<Key_T, Widget_T>... >

usuario6559931
fuente
FWIW, esto se ha convertido en sintaxis estándar en C ++ 20.
LF
2

He estado jugando con la última version 5.0.1compilación de clang con la -std=c++17bandera y ahora hay un buen soporte para los parámetros de tipo automático para lambdas:

#include <iostream>
#include <vector>
#include <stdexcept>

int main() {
    auto slice = [](auto input, int beg, int end) {
        using T = decltype(input);
        const auto size = input.size();
        if (beg > size || end > size || beg < 0 || end < 0) {
            throw std::out_of_range("beg/end must be between [0, input.size())");
        }
        if (beg > end) {
            throw std::invalid_argument("beg must be less than end");
        }
        return T(input.begin() + beg, input.begin() + end);
    };
    auto v = std::vector<int> { 1,2,3,4,5 };
    for (auto e : slice(v, 1, 4)) {
        std::cout << e << " ";
    }
    std::cout << std::endl;
}
Doug Coburn
fuente
1

Aquí hay una solución que implica envolver la lamba en una estructura:

template <typename T>                                                   
struct LamT                                                             
{                                                                       
   static void Go()                                                     
   {                                                                    
      auto lam = []()                                                   
      {                                                                 
         T var;                                                         
         std::cout << "lam, type = " << typeid(var).name() << std::endl;
      };                                                                

      lam();                                                            
   }                                                                    
};   

Para usar do:

LamT<int>::Go();  
LamT<char>::Go(); 
#This prints 
lam, type = i
lam, type = c

El problema principal con esto (además del tipeo adicional) no puede incrustar esta definición de estructura dentro de otro método u obtener (gcc 4.9)

error: a template declaration cannot appear at block scope

También intenté hacer esto:

template <typename T> using LamdaT = decltype(                          
   [](void)                                                          
   {                                                                 
       std::cout << "LambT type = " << typeid(T).name() << std::endl;  
   });

Con la esperanza de poder usarlo así:

LamdaT<int>();      
LamdaT<char>();

Pero me sale el error del compilador:

error: lambda-expression in unevaluated context

Así que esto no funciona ... pero incluso si compilara, sería de uso limitado porque aún tendríamos que poner el "uso de LamdaT" en el alcance del archivo (porque es una plantilla) que de alguna manera frustra el propósito de lambdas

rmccabe3701
fuente
1

No estoy seguro de por qué nadie más ha sugerido esto, pero puede escribir una función con plantilla que devuelva funciones lambda. Lo siguiente resolvió mi problema, la razón por la que vine a esta página:

template <typename DATUM>
std::function<double(DATUM)> makeUnweighted() {
  return [](DATUM datum){return 1.0;};
}

Ahora, cuando quiero una función que tome un tipo dado de argumento (por ejemplo std::string), solo digo

auto f = makeUnweighted<std::string>()

Y ahora f("any string")vuelve 1.0.

Ese es un ejemplo de lo que quiero decir con "función lambda con plantilla". (Este caso particular se usa para proporcionar automáticamente una función de ponderación inerte cuando alguien no quiere ponderar sus datos, sean cuales sean sus datos).

Jim Pivarski
fuente
2
Esto solo funciona si conoce el tipo de argumento de la lambda antes de crear la lambda, en cuyo caso puede usar una lambda con el tipo específico como argumento. El objetivo de lambda polimórfica es proporcionar trabajo para realizar en un tipo de argumento que nunca se sabe cuando se escribe el código de trabajo. Básicamente, esto es totalmente diferente, por lo que no se sugirió.
Klaim
Ah, claro, lo tengo. No pensé en ese caso de uso --- Pienso en las funciones lambda como cosas sobre la marcha y ese tipo de polimorfismo como algo en una biblioteca multipropósito. Estaba escribiendo una biblioteca con plantilla que necesita aceptar las funciones lambda del usuario de cualquier tipo y también proporcionar valores predeterminados del tipo correcto.
Jim Pivarski