2011-03-04 27 views
30

Estoy utilizando una biblioteca que define operadores de flujo de salida (operador < <) en el espacio de nombres global. En mi propio espacio de nombres, siempre estaba declarando esos operadores en el espacio de nombres global, y nunca tuve problemas con ellos. Pero ahora, por varias razones, necesito declarar esos operadores en mi propio espacio de nombres, y de repente, parece que el compilador ya no puede encontrar los operadores declarados en la biblioteca.Espacios de nombres y resolución de operador

Aquí está un ejemplo simple que ilustra mi problema:

#include <iostream> 

namespace A 
{ 
    struct MyClass {}; 
} 

std::ostream & operator<<(std::ostream & os, const A::MyClass &) 
    { os << "namespace A"; return os; } 

namespace B 
{ 
    struct MyClass {}; 

    std::ostream & operator<<(std::ostream & os, const B::MyClass &) 
     { os << "namespace B"; return os; } 
} 

namespace B 
{ 
    void Test() 
    { 
     std::cout << A::MyClass() << std::endl; 
     std::cout << B::MyClass() << std::endl; 
    } 
} 

int main() 
{ 
    B::Test(); 
    return 1; 
} 

estoy consiguiendo el error siguiente:

error: no match for ‘operator<<’ in ‘std::cout << A::MyClass()’ 

Tenga en cuenta que si ambos operadores están dentro de los espacios de nombres, o, alternativamente, si ambos están en el espacio de nombres global, el código se compila y ejecuta correctamente.

Realmente me gustaría entender qué está pasando y también cuál es la "buena práctica" para definir esos operadores con espacios de nombres.

Gracias!

+0

+1 ¡Muy extraño! Si pongo 'Test' en' namespace A', ¡se compila y ejecuta correctamente! –

Respuesta

0

Es porque su primera operator<<() se define fuera del espacio de nombres A.

+2

"Tenga en cuenta que si ambos operadores están dentro de los espacios de nombres, o alternativamente si ambos están en el espacio de nombres global, el código se compila y se ejecuta correctamente". Él está buscando un razonamiento detrás de por qué no funciona; él ya sabe cómo arreglarlo. –

31

Desde Test está dentro del espacio de nombres B la compilación ve el operador en ese espacio de nombres y toma nota de que no tiene una firma coincidente. También intenta encontrar el operador en el espacio de nombres A que contiene la clase pero tampoco la puede encontrar allí. Como ya existe un operador de este tipo (con la firma incorrecta) en el espacio de nombres B, no irá e intentará encontrar uno en el alcance global.

La razón por la que no busca la global es más o menos la siguiente. Primero voy a citar el estándar y luego trato de explicarlo.

de 3,4/1:

...Name lookup may associate more than one declaration with a name if it finds the name to be a function name; the declarations are said to form a set of overloaded functions (13.1). Overload resolution (13.3) takes place after name lookup has succeeded.

Al leer esto, cuando el compilador está tratando de encontrar una función (que sus operadores es en este contexto) primero intenta hacer la búsqueda de nombres para encontrar la función primero. Luego, a continuación, intenta elegir la función correcta del conjunto de sobrecargas.

Ahora desde 3.4.1/6:

A name used in the definition of a function(26) that is a member of namespace N (where, only for the purpose of exposition, N could represent the global scope) shall be declared before its use in the block in which it is used or in one of its enclosing blocks (6.3) or, shall be declared before its use in namespace N or, if N is a nested namespace, shall be declared before its use in one of N’s enclosing namespaces.

Vamos a descomponerlo. Está utilizando el operator<< en una función de nivel de espacio de nombres así que esta sección se aplica. Intentará encontrar ese operador usando la precedencia en el descrito arriba. Su operador no está declarado en el bloque actual ni en los bloques adjuntos (esto se refiere a {} anidados dentro de su función). Sin embargo, la siguiente parte coincide con "... se declarará antes de su uso en el espacio de nombres N ...". Hay es de hecho un operator<< en el espacio de nombre actual (B) por lo que agrega ese operador a su lista de coincidencias. No hay más coincidencias en B, y como el ámbito del mismo nombre se considera la mejor coincidencia posible, no buscará en ningún otro ámbito.

La razón por la que funciona cuando coloca el operador en el espacio de nombres A es que dado que el elemento que se está imprimiendo es miembro de A, ese espacio de nombres se considera porque está incluido en los espacios de nombres de la expresión.Dado que el espacio de nombres Aes considera que encuentra la coincidencia adecuada en ese espacio de nombres y se compila correctamente.

Ahora que tiene una lista de posibles operadores, intenta hacer una resolución de sobrecarga en ellos. Lamentablemente, el encontrado en el espacio de nombres B es el único que considera y no coincide con los argumentos requeridos.

En general, debe tener los operadores de inserción en el mismo espacio de nombres que la clase en la que opera.

+1

+1. ¡Mencione también la importancia/función de 'Koenig Lookup' aquí! – Nawaz

+1

Sí, hay un operador << en el espacio de nombres B, pero no tiene la firma correcta, entonces, ¿por qué el compilador no busca en el espacio de nombres global? Sería si B :: operator << no estaba allí – chataign

+0

Pones el código en espacios de nombres por algún motivo. ¡Realmente no queremos colisiones con todos los nombres en el espacio de nombres global! –

8

Mi respuesta es muy similar a las otras, pero en particular, el compilador está tratando de encontrar el operador A :: < <() porque está funcionando en algo en el espacio de nombres A. Si desea invocar el uno fuera del espacio de nombres, se le puede llamar de forma explícita mediante el uso de

::operator<<(std::cout, A::MyClass(); 

Para su uso sintáctico más suave, lo puso en el espacio de nombres.

5

El problema ha sido explicado en la respuesta por @Mark B. Lo siguiente resuelve el problema. En el espacio de nombres en el que desea utilizar el mundial operator<<, escriba el siguiente código:

using ::operator<<; 

En el ejemplo de código de la OP esa línea de código iría a la ubicación a lo largo del resto del código que declara/define operator<< para namespace B:

namespace B 
{ 
    struct MyClass {}; 

    std::ostream & operator<<(std::ostream & os, const B::MyClass &) 
     { os << "namespace B"; return os; } 

    using ::operator<<; 
} 
+0

Personalmente, preferiría no utilizar el operador global en general junto con la definición de clase , más bien solo cuando se necesita para la resolución (por lo tanto, en el ejemplo de ejemplo de la pregunta, antes de la función 'void Test()'). Claro, podríamos tener que hacer esto varias veces, todavía me parece más limpio ... – Aconcagua