2008-10-08 14 views
10

En el Google C++ Style Guide, el section on Operator Overloading recomienda no sobrecargar los operadores ("excepto en raras circunstancias especiales"). En concreto, se recomienda:Comparación de tipos de funciones frente al operador <

En particular, no sobrecargue operator== o operator< sólo para que su clase se puede utilizar como una clave en un recipiente STL; en su lugar, debe crear igualdad y tipos de funcion de comparación al declarar el contenedor.

Soy un poco borroso en lo que un funtor como se vería, pero mi principal pregunta es, ¿por qué habría que desea escribir sus propias palabras funcionales para esto? ¿No sería más simple definir operator< y usar la función estándar std::less<T>? ¿Hay alguna ventaja al usar una sobre la otra?

+0

La Guía de estilo de Google es demasiado restrictiva.También deja de usar 'cout' y' cin'. – rlbond

Respuesta

16

Excepto por los tipos más fundamentales, la operación inferior a la mínima no siempre es trivial, e incluso la igualdad puede variar de una situación a otra.

Imagine la situación de una aerolínea que desea asignar a todos los pasajeros un número de embarque. Este número refleja el orden de embarque (por supuesto). Ahora, ¿qué determina quién viene antes de quién? Puede tomar el orden en el que los clientes se registraron; en ese caso, la operación inferior compararía los tiempos de check-in. También podría considerar el precio que los clientes pagaron por sus boletos, menos de lo que ahora compararía los precios de los boletos.

... y así sucesivamente. Con todo, no es significativo definir un operator < en la clase Passenger aunque puede ser necesario tener pasajeros en un contenedor ordenado. Creo que eso es contra lo que Google advierte.

+0

Estoy de acuerdo. Extender: "Raras, circunstancias especiales" es relativo, por supuesto, si estás haciendo todas las clases de matemáticas es lo opuesto a raro y especial ... Creo que todos los programadores necesitan tomar (y dar) su consejo con unos pocos granos de sal . No hay una mejor manera de hacer nada (grano de sal: en algunos casos excepcionales, especiales) =) – SinisterRainbow

5

Bueno, de acuerdo a la página web que usted cita, no hay mucha ventaja para funtores ("[operadores] puede engañar a nuestra intuición en el pensamiento de que las operaciones costosas son baratos, las operaciones integradas.")

Mi tala es que uno debe esforzarse por hacer que las clases de objetos de primera clase tanto como sea posible, lo que, para mí, significa tener que entender tantos operadores como sensato.

Ha sido un tiempo desde que he escrito un funtor, pero sería algo como esto:

class MyClass {....} 

class LessThanMyClass : std:binary_function<MyClass, MyClass, bool> 
{ 
    public bool operator()(MyClass lhs, MyClass rhs) 
    { return /* determine if lhs < rhs */ ; } 
} 

vector<MyClass> objs; 
std::sort(objs.begin(), objs.end(), LessThanMyClass()); 

}

2

Un funtor es una clase con un operator(). En este caso, el método tomaría dos parámetros del tipo que se comparaba y arrojaría un resultado de bool si el primero es menor que el segundo.

Editar: para construir sobre lo que James Curran dijo, puede definir su functor dentro de la clase. Así, por ejemplo:

class MyClass 
{ 
    struct LessThan : public std::binary_function<MyClass, MyClass, bool> 
    { 
     bool operator()(const MyClass & first, const MyClass & second) const 
     { 
      return first.key < second.key; 
     } 
    }; 
}; 
1

Irónicamente, un funtor también requiere anulando un operador (el operador de llamada de función - operator()), así que no estoy seguro de lo que es su punto.

6

Generalmente, definir operator< es mejor y más simple.

El caso en el que desearía usar funtores es cuando necesita múltiples formas de comparar un tipo particular. Por ejemplo:

class Person; 

struct CompareByHeight { 
    bool operator()(const Person &a, const Person &b); 
}; 

struct CompareByWeight { 
    bool operator()(const Person &a, const Person &b); 
}; 

En este caso no puede haber una manera buena "default" para comparar y ordenar la gente por lo que no define operator< y el uso de palabras funcionales puede ser mejor. También podría decir que, en general, las personas se ordenan por altura, por lo que operator< solo llama al CompareByHeight, y cualquier persona que necesite que se ordenen las personas por peso tiene que usar CompareByWeight explícitamente.

Muchas veces el problema es que la definición de los funtores queda en manos del usuario de la clase, por lo que se tiende a obtener muchas redefiniciones de la misma cosa, siempre que la clase necesite ser utilizada en un contenedor ordenado.

2

Probablemente no llegaría tan lejos como la guía de estilo de Google.

Creo que lo que están consiguiendo es que cuando sobrecargues operator< y operator==, estás tomando una decisión para cada uso del tipo, mientras que los tipos de funtores solo se aplican a esa colección.

Si lo único que necesita para los comparadores es colocar el elemento en una colección, entonces es mejor tener una función específica para ese contexto en lugar de los operadores que se aplicarían en todos los contextos.

Es posible que desee ordenar las órdenes de compra cronológicamente, pero, en general, tendría sentido compararlas por su precio total. Si sobrecargamos operator< para comparar las fechas de modo que podamos cargarlas en una colección, estamos introduciendo el riesgo de que otro cliente pueda hacer un mal uso de nuestra operator<, lo que puede pensar que compara los precios totales.

2

Creo que el mensaje detrás de no definir el operador < es que el pedido es una propiedad de la colección, no del objeto. Las diferentes colecciones de los mismos objetos pueden tener diferentes ordenamientos. Por lo tanto, debe usar un functor separado para especificar el tipo de colección en lugar del operador <.

En la práctica, sin embargo, muchas de sus clases pueden tener un orden natural y ese es el único pedido utilizado en las colecciones en su aplicación. En otros casos, el pedido puede no ser relevante para la aplicación, solo la colección para que pueda encontrar los artículos más adelante. En estos casos, tiene mucho sentido definir el operador <.

Recuerde, cuando diseñamos modelos de objetos, solo estamos modelando un subconjunto del mundo real. En el mundo real, puede haber muchas formas diferentes de clasificar objetos de la misma clase, pero en el dominio de la aplicación en el que trabajamos puede haber una que sea relevante.

Si el código evoluciona a necesitar un segundo orden que es tan relevante como la primera, la clase debe refactorizado para eliminar operador < y colocar ambas funciones de clasificación de palabras funcionales separadas. Esto muestra la intención de que ninguna clasificación es más importante que las otras.

Con respecto a los operadores aritméticos, no debe sobrecargar estos a menos que esté implementando un tipo aritmético.

Por supuesto, hay excepciones para cada regla. Si no sabe si debe hacer una excepción o no, probablemente no debería hacerlo. La experiencia será tu guía.

Cuestiones relacionadas