2010-02-17 13 views
24

Estoy especializado en 'menos' (predicado) para un tipo de datos.Especialización de 'plantilla <class _Tp> estructura std :: menos' en el espacio de nombres diferente

El código es el siguiente:

template<> 
struct std::less<DateTimeKey> 
{ 
    bool operator()(const DateTimeKey& k1, const DateTimeKey& k2) const 
    { 
     // Some code ... 
    } 
} 

Al compilar (g ++ 4.4.1 en Ubuntu 9.10), me sale el error:

La especialización de 'std :: plantilla de estructura menos' en diferentes espacio de nombres

que hice algunas investigaciones y encontró que había una 'solución' que involucró a envolver la especialización en un espacio de nombres std - es decir, cambiar el código para:

namespace std { 
template<> 
struct less<DateTimeKey> 
{ 
    bool operator()(const DateTimeKey& k1, const DateTimeKey& k2) const 
    { 
     // Some code ... 
    } 
} 
} 

que de hecho, cierra el compilador. Sin embargo, esa solución fue de un puesto de 5 años de edad (por el "gran" Victor Bazarof no menos [juego de palabras no intencionado]). ¿Sigue siendo esta solución el camino a seguir, o hay una mejor manera de resolver esto, o la "vieja manera" sigue siendo válida?

+1

Sobrecarga 'DateTimeKey :: operator <'? – kennytm

Respuesta

23

Esta sigue siendo la manera de hacerlo. Lamentablemente, no puede declarar ni definir funciones dentro de un espacio de nombres como lo haría con una clase: debe envolverlas en un bloque de espacio de nombres.

+0

Ok, gracias por la aclaración. Continuaré y realizaré los cambios –

3

El menos functor no tiene que estar en std namespace. Entonces

struct A 
{ 
    A(int _v=0):v(_v){} 
    int v; 
}; 


template<> struct less<A> 
{ 
    bool operator()(const A& k1, const A& k2) const 
    { 
     return k1.v < k2.v; 
    } 
}; 


std::map<A,int> m; 
m[A(1)] = 1; 
m[A(2)] = 2; 

Funciona como esperaba. (Llama al functor que acaba de crear).

Supongo que ya lo sabe, pero puede simplemente escribir su propio operador < (k1, k2), que es lo que busca el funtor menos predeterminado.

bool operator<(const DateTimeKey & k1, const DateTimeKey & k2) 
{ 
//your code... 
} 
+3

+1 en el operador de definición <'en lugar de redefinir menos –

23

Si necesita especializarse un algoritmo estándar, puede hacerlo en el espacio de nombres std. Es lo único que se le permite hacer dentro de ese espacio de nombres de acuerdo con el estándar.

[lib.reserved.names]/1

It is undefined for a C++ program to add declarations or definitions to namespace std or namespaces within namespace std unless otherwise specified. A program may add template specializations for any standard library template to namespace std. Such a specialization (complete or partial) of a standard library template results in undefined behavior unless the declaration depends on a user-defined name of external linkage and unless the specialization meets the standard library requirements for the original template

Ahora, la pregunta es si realmente se desea especializarse std::less. Tenga en cuenta que std::less llamará al operador de comparación definido para su tipo, por lo que puede proporcionar esa operación en lugar de especializar la plantilla.

El problema con la especialización std::less para su tipo particular es que causará confusión si proporciona una operación diferente a la realizada por operator< para su tipo. Si realizan la misma operación, simplemente deje la definición predeterminada std::less sin especialización.

Si no desea proporcionar el operador de comparación, pero aún desea utilizar el tipo en contenedores asociativos o con algoritmos que requieren un comparador, puede proporcionar un functor de comparación externo con otro nombre que no confunda a otros lectores (y usted en algún lugar en el futuro).

+0

+1 para el presupuesto, y tanto para responder la pregunta como para ofrecer una buena alternativa. – JonM

3

¿Por qué estás haciendo esto?

std::less existe sólo para dos propósitos:

  1. para dar un nombre a operador <, permitiendo que se pasa como un funtor
  2. para permitir explícitamente la comparación de dos punteros que no están en la misma matriz (que es técnicamente ilegal si se hace con punteros primas)

no hay ninguna razón para que un usuario sobrecargarla - ya sea la sobrecarga operator< o utilizar una función comparador personalizado.

Hay algoritmos estándar que pueden ser sensiblemente especializados - std::swap es un buen ejemplo, y para hacerlo debe declarar la especialización dentro del espacio de nombres estándar.

+2

No estoy seguro de que std :: swap sea un buen ejemplo, ya que el dinero inteligente es que no lo especializas, defines una función 'swap' independiente en el mismo espacio de nombres que el UDT, y dejas que ADL lo encuentre. La excepción en la que puedo pensar es si su clase tiene que trabajar con una plantilla que nunca obtuvo esa nota, y que llama 'std :: swap (t1, t2)' en lugar de 'using std :: swap; swap (t1, t2); '. No estoy seguro de si esta es una especialización sensata, o simplemente una especialización pragmática ;-) –

+0

Ha habido mucha discusión sobre las listas de correo de impulso sobre cuál es la mejor, razón por la cual boost :: swap se lleva a cabo tampoco. –

+1

'std :: less' existe por una tercera razón: a veces, el' operador '' de un tipo se comporta de forma ligeramente extraña. El mejor ejemplo son los números de punto flotante donde uno puede querer tratar '-0.0' diferente de' 0.0', o cuando uno quiera hacer 'nan' comparable. Otros ejemplos son tipos que solo tienen un orden parcial, y donde la notación '<' es una notación específica de dominio natural. En estos casos, la especialización 'std :: less' permite usar este tipo en conjuntos y mapas. –

Cuestiones relacionadas