2009-05-05 5 views
5

He estado jugando con functors en C++. En particular, tengo un vector de pares que me gustaría ordenar por el primer elemento del par. Comencé a escribir un functor completamente especializado (es decir, algo así como "bool MyLessThan (MyPair & lhs, MyPair & rhs)"). Entonces, solo porque este tipo de cosas son interesantes, quería intentar escribir un genérico "Aplicar F a los primeros elementos de este par". Escribí el siguiente, pero a g ++ no le gusta. Me sale:g ++ rechaza mi functor simple con "esperado un tipo, tengo 'xyz'"

de error: Tipo/valor de desajuste en el argumento 2 en la lista de parámetros de plantilla para 'plantilla struct Pair1stFunc2' de error: se esperaba un tipo, tiene 'menos'

#include <algorithm> 
#include <functional> 
#include <utility> 
#include <vector> 

template <class P, class F> 
struct Pair1stFunc2 
{ 
    typename F::result_type operator()(P &lhs, P &rhs) const 
    { return F(lhs.first, rhs.first); } 

    typename F::result_type operator()(const P &lhs, const P &rhs) const 
    { return F(lhs.first, rhs.first); } 
}; 

typedef std::pair<int,int> MyPair; 
typedef std::vector<MyPair> MyPairList; 

MyPairList pairs; 

void foo(void) 
{ 
    std::sort(pairs.begin(), 
       pairs.end(), 
       Pair1stFunc2<MyPair, std::less>()); 
} 

¿Alguien puede arrojar alguna luz sobre ¿Qué estoy haciendo mal aquí? Sé que esto es un ejemplo ligeramente artificial, pero me gustaría saber qué está pasando, aunque solo sea para mejorar mi STL-fu.

+2

Una vez que resuelva este problema, tendrá otro porque F es un tipo. Estás construyendo una nueva F usando lhs.first y rhs.first. Encontrará que std :: less no tiene un constructor de dos argumentos. Necesitas tener un * valor * de tipo F. –

+0

Aquí hay algo para mejorar tu boost :: bind-fu: sort (p.begin(), p.end(), bind (menos (), bind (y MyPair: : primero, _1), bind (& MyPair :: first, _2))); ;) http://codepad.org/NouR5fko –

Respuesta

2

Necesita especializar std :: less con el tipo de comparación que está utilizando.

Pair1stFunc2<MyPair, std::less<int> >() 

harán el truco. Dentro de su propio operador() también necesitará crear una instancia de un objeto del tipo de comparación, ya que no puede simplemente llamar a la clase directamente. P.ej. cambiar

return F(lhs.first, rhs.first); 

a

F func; 
return func(lhs.first, rhs.first); 

También puede mover la especialización en el funtor, como sugiere otra respuesta.

+0

Sin embargo, en realidad no usa std :: less con MyPair, pero std :: less . –

+0

Muy bien, error de cerebro de mi parte – Nik

3

Tenga en cuenta que std::less es en sí mismo una plantilla y no especifica el parámetro de plantilla de plantilla cuando lo llama desde foo() función sort! Aquí less es un tipo incompleto y de ahí el problema.

6

Para ampliar la respuesta de dirkgently, aquí está un ejemplo de lo que podría funcionar como deseaba:

template <typename T, template <typename> class F> 
struct Pair1stFunc2 
{ 
    template <typename P> 
    typename F<T>::result_type operator()(P &lhs, P &rhs) const 
    { F<T> f; return f(lhs.first, rhs.first); } 

    template <typename P> 
    typename F<T>::result_type operator()(const P &lhs, const P &rhs) const 
    { F<T> f; return f(lhs.first, rhs.first); } 
}; 

void foo(void) 
{ 
    std::sort(pairs.begin(), 
       pairs.end(), 
       Pair1stFunc2<int, std::less>()); 
} 

Tenga en cuenta que funciona, pero puede que no sea exactamente lo que tenía en mente.

+2

http://codepad.org/54PXQSoL –

+0

+1 para plantillas no especificadas como parámetros de plantilla. – mmmmmmmm

+0

Esto funciona, pero los parámetros de la plantilla de plantilla son realmente innecesarios aquí porque solo se refiere a F como "F " - vea la respuesta de Martin York, que también funcionará para los comparadores sin plantilla. –

1

La solución más sencilla sería la de indicar lo que quiere como un argumento, una función con una firma adecuada:

template<typename P, bool (*F)(P,P)> struct Pair1stFunc2 { ... } 

En este caso, pasar una plantilla de función como segundo argumento hará que la resolución de sobrecarga debe hecho en él con P, P como tipos de argumentos. Esto funciona porque se mueve la resolución de sobrecarga de struct Pair1stFunc2::operator()

También desea que la posibilidad de pasar de una funtor, pero los que sí es necesario que se pasa como argumento de tipo de plantilla y luego creada en el interior del operador():

typename F::result_type operator()(const P &lhs, const P &rhs) const 
{ return F()(lhs.first, rhs.first); } 

Aquí, F es el tipo de functor y F() una instancia de ese functor.

El tercer caso ya está cubierto anteriormente, la plantilla del functor. std :: less es una plantilla así. En ese caso, necesita un argumento de plantilla de plantilla.

+0

Básicamente correcto, pero Yo diría que las instancias de std :: less son functors. Se puede llamar a un functor utilizando la sintaxis F (argument-list). No puedes llamar a std :: less así. – MSalters

+0

(eliminó mi comentario porque se refería a la respuesta antes de ser editado) –

+0

¿Está proponiendo dos sintaxis separadas para Pair1stFunc2 :: operator() (lhs, rhs), una para punteros de función, otra para funtores? Por supuesto, su solución usando un parámetro de plantilla de puntero a función debería ser más pequeña y más rápida para ese caso, pero puede usar una sola sintaxis haciendo que Pair1stFunc2 tome dos plantillas * tipo * parámetros (P y F) y almacene un miembro f de tipo F (esto se puede llenar en un ctor de 1 arg para el caso del puntero de función). Entonces el operador() (lhs, rhs) puede decir "return f (lhs.first, rhs.first);". –

2

Parecido a unwesen. Pero no necesita usar plantillas de plantilla.

#include <algorithm> 
#include <functional> 
#include <memory> 
#include <vector> 

typedef std::pair<int,int> MyPair; 
typedef std::vector<MyPair> MyPairList; 
MyPairList pairs; 


// Same as original. 
template <typename T,typename F> 
struct Pair1stFunc2 
{ 
    template <typename P> 
    typename F::result_type operator()(P &lhs, P &rhs) const 
    { F f; // Just need to create an anstance of the functor to use. 
     return f(lhs.first, rhs.first); } 

    template <typename P> 
    typename F::result_type operator()(const P &lhs, const P &rhs) const 
    { F f; // Just need to create an anstance of the functor to use. 
     return f(lhs.first, rhs.first); } 
}; 


void foo(void) 
{ 
    std::sort(pairs.begin(), 
       pairs.end(), 
       Pair1stFunc2<int, std::less<int> >()); // initialize the version of less 
} 
+0

+1. Esto es más general que la solución de unwesel porque también funcionará para los comparadores sin plantilla. –

Cuestiones relacionadas