c++ - expressions - ¿Pueden las funciones lambda ser modeladas?
java 8 lambda explicacion (10)
En C ++ 11, ¿hay alguna manera de crear una plantilla de una función lambda? ¿O es inherentemente demasiado específico para ser templado?
Entiendo que puedo definir una clase / functor con plantilla clásica en su lugar, pero la pregunta es más parecida a la siguiente: ¿el lenguaje permite la creación de plantillas de funciones lambda?
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 han lanzado este año y ahora proporcionan lambdas polimórficos con la misma sintaxis que en este ejemplo. Algunos compiladores importantes ya lo implementan.
En eso se encuentra (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 la situación de este 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). ¿Puede invocar la bar(x)
? ¿Qué restricciones tiene el lambda (después de todo, el parámetro es solo una plantilla)?
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 invoca) y esas cosas. Más simple era simplemente dejarlo todo y apegarse a las lambdas monomórficas.
Sin embargo, con la eliminación de los conceptos de C ++ 0x, las lambdas polimórficas se convierten en una proposición simple nuevamente. Sin embargo, no puedo encontrar ninguna propuesta para ello. :(
Aquí hay una solución que implica envolver el 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 principal problema con esto (además del tipeo extra) no puede incrustar esta definición de estructura dentro de otro método o usted obtiene (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 obtengo el error del compilador:
error: lambda-expression in unevaluated context
Entonces 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 forma frustra el propósito de lambdas.
C ++ 11 lambdas no puede ser decltype()
como se indica en otras respuestas, pero decltype()
parece ayudar cuando se utiliza 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 de plantilla, pero me doy cuenta de que todavía significa que las lambdas no pueden ser modeladas.
Eche un vistazo a Boost.Phoenix para lambdas polimórficas: http://www.boost.org/doc/libs/1_44_0/libs/spirit/phoenix/doc/html/index.html No requiere C ++ 0x, por el camino :)
En C ++ 11, las funciones lambda no se pueden modelar, pero en la próxima versión del estándar ISO C ++ (a menudo llamado C ++ 14), se introducirá esta característica. [Source]
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 del tipo no usará las reglas de deducción de tipo auto
, sino que usará las reglas de la deducción del argumento de la plantilla. También vea la propuesta de expresiones lambda genéricas (y la update de esto).
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 _widgets
es std::tuple< fusion::pair<Key_T, Widget_T>... >
He estado jugando con la última version 5.0.1
clang version 5.0.1
compilando con la -std=c++17
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;
}
Me pregunto qué tal esto:
template <class something>
inline std::function<void()> templateLamda() {
return [](){ std::cout << something.memberfunc() };
}
Usé un código similar como este para generar una plantilla y me pregunto si el compilador optimizará la función de "envoltura".
No estoy seguro de por qué nadie más ha sugerido esto, pero puede escribir una función de 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, cada vez que quiero una función que toma un tipo dado de argumento (por ejemplo, std::string
), solo digo
auto f = makeUnweighted<std::string>()
y ahora f("any string")
devuelve 1.0
.
Ese es un ejemplo de lo que quiero decir con "función lambda con plantilla". (Este caso particular se utiliza para proporcionar automáticamente una función de ponderación inerte cuando alguien no quiere ponderar sus datos, cualesquiera que sean sus datos).
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 se llaman Gendered Lambdas.
[info] La mayoría de los compiladores populares ahora son compatibles con esta característica. Microsoft Visual Studio 2015 es compatible. Clang apoya. GCC es compatible.