2010-10-08 18 views
8

Duplicar posible:
Where should non-member operator overloads be placed?La sobrecarga de operadores y espacios de nombres

durante la navegación en SO, a menudo encuentro pregunta o respuesta que implica la sobrecarga/definen un std::ostream& operator<<(std::ostream& os, const Foo& foo) o una Foo operator+(const Foo& l, const Foo& r).

Si bien sé cómo y cuándo (no) escribir estos operadores, estoy confundido acerca de la cosa namespace.

Si tengo la clase siguiente:

namespace bar 
{ 
    class Foo {}; 
} 

En lo que namespace debo escribir las diferentes definiciones de operador?

// Should it be this 

namespace bar 
{ 
    std::ostream& operator<<(std::ostream& os, const Foo& foo); 
} 

// Or this ? 

namespace std 
{ 
    ostream& operator<<(ostream& os, const bar::Foo& foo); 
} 

// Or this ? 

std::ostream& operator<<(std::ostream& os, const bar::Foo& foo); 

La misma pregunta se aplica al operator+. Entonces, ¿cuál es la buena práctica aquí y ¿por qué?

+1

duplicados de [¿Dónde debe se colocan sobrecargas del operador no miembro?] (http: // stackoverflow.com/questions/3623631/overloading-operator) –

+0

@James McNellis: De hecho, no pude encontrar una pregunta similar. Gracias;) – ereOn

+1

Tenga en cuenta que el segundo es realmente ilegal, solo puede agregar especializaciones al espacio de nombres 'std'. – GManNickG

Respuesta

9

Debe estar en el espacio de nombre bar. Debe considerar what makes up the interface for the class, y agruparlos.

"Una clase describe un conjunto de datos junto con las funciones que operan en esos datos". Su función gratuita funciona en un Foo, por lo tanto, es parte de Foo. Se debe agrupar con Foo en el espacio de nombres bar.

Argument-dependent lookup, o ADL, encontrará la función.

También sabemos que deberíamos prefer non-friend non-member functions. Lo que esto significa es que, en general, sus clases tendrán su definición y funciones miembro, seguidas inmediatamente por funciones gratuitas que operan en la clase.

+0

Tiene sentido y también es fácil de recordar. Por una razón oscura, no he considerado esas sobrecargas como "funciones libres", que sí lo son. Gracias por las explicaciones. – ereOn

12

La regla es que, cuando se busca una sobrecarga de función adecuada, se consideran tanto el espacio de nombres actual como todos los espacios de nombres de las definiciones de tipo de argumento. Esto se llama Argument Dependent Lookup (ADL).

Así que cuando usted tiene este código:

::std::ostream& os = /* something */; 
    const ::bar::Foo& foo = /* something */; 
    os << foo; 

Se consideran los siguientes espacios de nombres:

  • El espacio de nombres actual
  • :: std, porque el tipo O se define allí
  • :: bar, porque el tipo de foo se define allí

Por lo tanto, las tres posibilidades que haya nombrado funcionarán y, por lo tanto, son "lo suficientemente buenas" a primera vista.

Sin embargo ....

No se le permite definir nuevas funciones en std ::, por lo que no puede poner su operador sobrecargado en ese espacio de nombres.(Se le permite especializar plantillas en :: std, pero eso no es lo que estamos haciendo aquí)

En segundo lugar, el "espacio de nombre actual" puede cambiar, por lo que si coloca la definición de la función en ese espacio de nombres, no siempre ser encontrado.

Así que al final, el mejor lugar para poner el operador sobrecargado está en el mismo espacio de nombres como Foo:

namespace bar 
{ 
    std::ostream& operator<<(std::ostream& os, const Foo& foo); 
} 
+0

Agradable y preciso. Gracias. Aquí está mi +1. – ereOn

0

La mejor opción es la opción 1. ¿Por qué? Porque cuando utiliza un nombre de función no calificado (un operador sobrecargado es una función), además de la búsqueda de nombre normal, se aplica la búsqueda Dependiente del argumento, es decir (informalmente) se buscan todos los espacios de nombres donde se declararon los argumentos. P. ej.

namespace N 
{ 
    class X(){}; 
    void f(X){} 
} 
int main() 
{ 
    N::X x; 
    f(x); //works fine, no need to qualify f like N::f 
} 

Lo mismo ocurre con los operadores.

Por otro lado, en el caso de la opción 2, el operador aún se encontrará porque ostream está en std (misma regla de ADL). Pero no es una buena idea agregar cosas al espacio de nombres estándar.

Y la tercera opción es mala, estilísticamente, ¿por qué hacerlo si la primera opción es suficiente?

Así que, sin duda la opción 1.

HTH.

0

La buena práctica es declarar a los operadores (no miembros) en el mismo espacio de nombres como la clase a la que pertenecen.

Para algo como operator+, esto es bastante fácil: funciona solo en objetos Foo, por lo que debe ir en el mismo espacio de nombres que Foo. Para operator<< y operator>>, aún puede elegir entre los espacios de nombres std y bar. En primer lugar, no debe agregar sobrecargas de funciones/operadores al espacio de nombres std. Y en segundo lugar, la parte importante de estas sobrecargas no es que trabajen con un flujo, sino que leen/escriben un objeto Foo. Entonces tiene más sentido agruparlo con la clase Foo.

También se debe tener en cuenta que las reglas de C++ están diseñadas de tal manera que los operadores sobrecargados que están definidos en el mismo espacio de nombres que la clase en la que operan casi siempre se encontrarán correctamente, aunque esto irá mucho más a menudo si los operadores están declarados en algún otro espacio de nombres no relacionado.

2

Para que la sobrecarga del operador funcione correctamente, la función debe ser en el mismo espacio de nombres que uno de sus operandos. De lo contrario, ADL no lo encuentra. Esto significa el espacio de nombres de su clase para operadores como + y -. En teoría, puede poner el operador < < en el estándar o en el mismo espacio de nombres que su clase, pero el estándar prohíbe definir nuevas funciones en std, así que aquí también, lo pone en el mismo espacio de nombres que la clase.

(Y, por supuesto, que no suelen aplicar + o -, pero + = y - =, y luego se derivan de una plantilla que proporciona + y -. Automáticamente)