2011-10-06 13 views
5

Actualmente estoy haciendo algunos ejercicios con funciones de plantilla. Tuve la tarea de escribir una implementación del algoritmo de transformación. lo hice como el siguiente y funciona:lambda con plantillas

template <class in, class out, class T> 
out stransform(in b, in e, out d, T p(const T&)) { 
    while (b != e) 
     *d++ = p(*b++); 
    return d; 
} 

Como con la normal transformar tengo que llamar el predicado con un tipo explícito como

stransform(begin(vec1), end(vec1), back_inserter(vec2), predi<double>); 

Ahora, me topé con el C++ 11 lambdas y quería llamar a mi función como esta:

stransform(begin(vec1), end(vec1), back_inserter(vec2), [] (double x) ->double {return x * 10;}); 

con eso me pongo un error del compilador que el tipo no puede ser deducido. Esto es lo que no entiendo, ya que estoy definiendo el tipo T en mi lambda en realidad dos veces.

También revisé la función de transformación original, con la que está trabajando. Luego revisé el implementation de ese y obviamente se implementa con una clase de plantilla para toda la función. ¿Es esa la forma correcta de implementar predicados con plantillas?

Respuesta

13

El predicado es normalmente un simple argumento de plantilla:

template <class in, class out, class UnaryPredicate> 
out stransform(in b, in e, out d, UnaryPredicate p); 

Esto aceptará punteros para funcionar, lambdas y objetos de función.

+0

Pensé que después de leer la implementación de la transformación, eso hace que sea mucho más fácil también. Todavía me pregunto por qué no trabajo en el otro caso. – inf

+5

@bamboon Supongo que una lambda no es una función sino un objeto de función, ya que también puede mantener el estado (es un cierre y no solo una función). –

+1

Tenga en cuenta que una lambda que no realiza ninguna captura es convertible a una función ordinaria (puntero). – spraff

2

T p(const T&) es el tipo de una función que toma Tpor referencia. Su lambda toma su argumento por valor.

stransform (
    begin (vec1), 
    end (vec1), 
    back_inserter (vec2), 
    [] (const double & x) -> double { 
     return x * 10; 
    }); 

Esto debería funcionar. Una lambda que no realiza ninguna captura es convertible en una función ordinaria.

+1

, pero ¿por qué confiar en una conversión innecesaria al puntero de función, cuando podría simplemente redefinir el functor para trabajar con funtores y punteros de función? – jalf

+0

tampoco funciona para mí (usando msvc2011). – inf

+0

@jalf Redefinir el functor es una mejor solución, estoy de acuerdo. Si este problema surgió con el código de la biblioteca inmutable, la conversión puede funcionar. – spraff

2

se implementa obviamente con una clase de plantilla para toda la función

Leve a un lado de la terminología apropiada: std::transformes una plantilla de función, no una función. Más importante aún, en una declaración del estilo

template<class InputIterator, class OutputIterator, class Functor> 
OutputIterator 
transform(InputIterator begin, InputIterator end, OutputIterator out, Functor f); 

parámetros de la plantilla InputIterator, OutputIterator y Functor no están obligados a ser los tipos de clases. Considere este ejemplo:

// function declaration 
int increment(int); 

int array[] = { 0, 1, 2, 3, 4 }; 
transform(std::begin(array), std::end(array), std::begin(array), increment); 

Entonces InputIterator y OutputIterator se deducen ser int* y Functor es int(*)(int), ninguno de los cuales es un tipo de clase - y mucho menos una clase de plantilla, pero estoy divagando. Y de hecho, transform podría también ser declarado

template<typename InputIterator, typename OutputIterator, typename Functor> 
OutputIterator 
transform(InputIterator begin, InputIterator end, OutputIterator out, Functor f); 

donde la palabra clave typename es un poco más clara de la naturaleza de los parámetros de plantilla: son tipos, de cualquier naturaleza.