2012-06-21 14 views
6

¿Es posible utilizar un argumento de tipo de plantilla desde una función de plantilla circundante dentro de una función anónima local? Estoy bastante seguro de que no se puede declarar una plantilla lambda ...¿Cómo usar el argumento de tipo de plantilla en lambda?

Por ejemplo ¿cómo voy a ir haciendo algo como esto:

template <typename T> 
void TrimString(std::basic_string<T>& str, const std::locale& loc = std::locale()) 
{ 
    // std::isspace as lambda unary predicate? 
    auto fn = [&loc](T c){ return std::use_facet<std::ctype<T>>(loc).is(std::ctype_base::space, c); }; 
    // trim right 
    str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(fn)).base(), str.end()); 
    // trim left 
    str.erase(str.begin(), std::find_if(str.begin(), str.end(), std::not1(fn))); 
} 

Actualmente esto genera el siguiente error:

error C2039: 'argument_type' : is not a member of '`anonymous-namespace'::<lambda0>' 

Lo cual tiene sentido ya que la lambda no tiene idea sobre el argumento T de la función de plantilla circundante.

Uso VS2010 y gcc 4.7 pero no quiero utilizar boost.

¿Alguna idea?

Editar: Parece que estaba equivocado en mi suposición de que el problema era el argumento de la plantilla en sí. Más bien es el uso de std::not1 compilado con la función lambda. Aquí está la salida de error más detallado:

error C2039: 'argument_type' : is not a member of '`anonymous-namespace'::<lambda0>' 
: see declaration of '`anonymous-namespace'::<lambda0>' 
: see reference to class template instantiation 'std::unary_negate<_Fn1>' being compiled 
      with 
      [ 
       _Fn1=`anonymous-namespace'::<lambda0> 
      ] 
: see reference to function template instantiation 'void TrimString<char>(std::basic_string<_Elem,_Traits,_Ax> &,const std::locale &)' being compiled 
      with 
      [ 
       _Elem=char, 
       _Traits=std::char_traits<char>, 
       _Ax=std::allocator<char> 
      ] 

¿Es necesario declarar explícitamente el tipo de argumento si se trata de un tipo de función? No estoy seguro de lo que estoy haciendo mal ... todavía

Respuestas:

Opción 1: Si yo no uso std::not1 y en lugar de negar el valor devuelto en la lambda consigo mismo comportamiento sin problema.

auto fn = [&loc](T c){ return !std::use_facet<std::ctype<T>>(loc).is(std::ctype_base::space, c); }; 

Opción 2: Desde el lambda no es ya equivalente a la forma std::isspace se comportaría como un predicado unario un constructor fundido objeto función también hace el truco.

str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(std::function<bool(T)>(fn))).base(), str.end()); 
+0

Por cierto, el mensaje de error parece indicar que el problema está en otra parte, específicamente con el lambda declarado en * namespace * scope. – Nawaz

+1

Casting 'fn' como este' std :: not1 (std :: function (fn)) 'también funciona. –

Respuesta

6

Definitivamente puede usar T como tipo de parámetro de una expresión lambda.El siguiente programa compila multas a GCC 4.5.1:

include <iostream> 

template<typename T> 
void f(T arg) 
{ 
    auto print = [](T a) { std::cout << a << std::endl; }; 
    print(arg); 
} 

int main() { 
     f(8899); 
     f("Nawaz"); 
     return 0; 
} 

Véase a sí mismo: http://ideone.com/l32Z6

Por cierto, el mensaje de error parece indicar que el problema radica en otro lugar, específicamente con una lambda declaró en espacio de nombres alcance:

error C2039: 'argument_type' : is not a member of '`anonymous-namespace'::<lambda0>'


Después de su edición, todo lo que puedo decir es que no haga use std::not1 luego. De hecho, ni siquiera lo necesitas. Puede usar return !whatever-expression en la propia lambda.

+0

Hmm ... ¿entonces el error de OP no es del argumento lambda? – Jason

+0

@Jason: Eso es lo que parece ser. – Nawaz

+0

Doh! Actualizaré la pregunta. – AJG85

2

Editar: Como @Nawaz señala, el error debe estar llegando forma en otro lugar ... lo que describo a continuación es una exageración ...

Usando decltype, se podría hacer algo como la siguiente:

template <typename T> 
void TrimString(std::basic_string<T>& str, 
       const std::locale& loc = std::locale(), 
       T arg = T()) 
{ 
    auto fn = [&loc](decltype(arg) c){ return std::use_facet<std::ctype<T>>(loc).is(std::ctype_base::space, c); }; 

    //...rest of your code 
} 

Esto es usando (o abusar) el hecho de que la expresión decltype(arg) evalúa el tipo de arg , que en este caso es el tipo de T.

+0

¿Por qué usar 'decltype' cuando' T' debería funcionar bien? Él debe estar haciendo otra cosa también. – Nawaz

+0

Estaba asumiendo que su error provenía de lo que dijo que era, que es el uso de 'T' en lugar de un tipo de argumento conocido ... obviamente su publicación muestra que no hay problema con eso ... – Jason

+0

Si capturo' arg' y use 'decltype' obtengo el mismo error. Obviamente, si no lo capturo, entonces se queja de la captura implícita o no hay modo de captura predeterminado. Esto podría estar en la línea correcta a pesar de que no funciona ... o MSVC está roto si esto funcionara. – AJG85

9

El problema no está causado por el uso de un parámetro de plantilla dentro de la lambda, ya que el parámetro ya se ha resuelto a un tipo en el momento en que se construye la lambda.

El problema es que la lambda que define no se puede combinar con std::not1, que requiere, como argumento, un std::unary_function<argument_type,return_type>.

La forma más fácil de resolver el problema es no utilizar std::not1, y en lugar de negar la predicación justo en la expresión lambda:

auto fn = [&loc](T c){ return !std::use_facet<std::ctype<T>>(loc).is(std::ctype_base::space,c); }; 

El código completo que compila y funciona con GCC 4.7.0 entonces se convierte en:

#include <string> 
#include <algorithm> 
#include <locale> 
#include <iostream> 

template <typename T> 
void TrimString(std::basic_string<T>& str, const std::locale& loc = std::locale()) 
{ 
    auto fn = [&loc](T c){ return !std::use_facet<std::ctype<T>>(loc).is(std::ctype_base::space,c); }; 

    str.erase(std::find_if(str.rbegin(), str.rend(),fn).base(), str.end()); 
    str.erase(str.begin(), std::find_if(str.begin(), str.end(), fn)); 
} 

int main() { 
    std::basic_string<char> s(" hello "); 
    TrimString(s); 
    std::cout << s << std::endl; 
    return 0; 
} 

Esto da salida a

hello 

como expe cted.

+0

¡Sí, acabo de llegar a esta conclusión yo mismo ahora que estaba viendo el problema real! – AJG85

Cuestiones relacionadas