namespace - overload operator c++




Espaces de noms et résolution des opérateurs (3)

J'utilise une bibliothèque qui définit les opérateurs de flux de sortie (opérateur <<) dans l'espace de noms global. Dans mon propre espace de noms, je déclarais toujours de tels opérateurs dans l'espace de noms global et je n'ai jamais eu de problèmes avec eux. Mais maintenant, pour diverses raisons, je dois déclarer ces opérateurs dans mon propre espace de noms et, tout à coup, le compilateur ne semble plus capable de trouver les opérateurs déclarés dans la bibliothèque.

Voici un exemple simple qui illustre mon problème:

#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;
}

Je reçois l'erreur suivante:

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

Notez que si les deux opérateurs sont situés dans les espaces de noms ou, le cas échéant, s'ils se trouvent tous les deux dans l'espace de noms global, le code est compilé et exécuté correctement.

J'aimerais vraiment comprendre ce qui se passe et aussi quelle est la "bonne pratique" pour définir de tels opérateurs avec des espaces de noms.

Merci!


C'est parce que votre premier operator<<() est défini en dehors de l'espace de noms A.


Le problème a été expliqué dans la réponse par @Mark B. Ce qui suit résout le problème. Dans l'espace de noms où vous souhaitez utiliser l' operator<< global operator<< , tapez le code suivant:

using ::operator<<;

Dans l'exemple de code de l'OP, cette ligne de code irait à l'emplacement le long de l'autre code qui déclare / définit l' operator<< pour l' namespace B de namespace B :

namespace B
{
   struct MyClass {};

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

   using ::operator<<;
}

Puisque Test trouve dans l’espace de noms B la compilation voit l’opérateur dans cet espace de noms et constate qu’il n’a pas de signature correspondante. Il tente également de trouver l'opérateur dans l'espace de noms A qui contient la classe, mais n'y trouve pas non plus. Comme il existe déjà un tel opérateur (avec la signature erronée) dans l'espace de noms B il ne tentera pas d'en trouver un de portée globale.

La raison pour laquelle il ne recherche pas le nom global est à peu près la suivante. Je vais d'abord citer la norme, puis essayer de l'expliquer.

À partir de 3.4 / 1:

... La recherche de nom peut associer un nom à plusieurs déclarations si le nom est un nom de fonction; on dit que les déclarations forment un ensemble de fonctions surchargées (13.1). La résolution de surcharge (13.3) a lieu une fois que la recherche de nom a réussi.

En lisant ceci, lorsque le compilateur essaie de trouver une fonction (que vos opérateurs exploitent dans ce contexte), il essaie d’abord de rechercher le nom pour trouver la fonction en premier. Ensuite, il essaie de choisir la bonne fonction dans l'ensemble des surcharges.

Maintenant à partir de 3.4.1 / 6:

Un nom utilisé dans la définition d'une fonction (26) membre de l'espace de noms N (où N ne peut représenter que la portée globale) doit être déclaré avant son utilisation dans le bloc dans lequel il est utilisé. ou dans l'un de ses blocs englobants (6.3) ou, doit être déclaré avant son utilisation dans l'espace de noms N ou, si N est un espace de noms imbriqué, doit être déclaré avant son utilisation dans l'un des N espaces de noms inclus.

Décomposons cela. Vous utilisez l' operator<< dans une fonction au niveau de l'espace de noms pour que cette section s'applique. Il va essayer de trouver cet opérateur en utilisant la priorité décrite ci-dessus. Votre opérateur n'est pas déclaré dans le bloc actuel ou dans les blocs englobants (il s'agit de {} imbriqué dans votre fonction). Cependant, la partie suivante correspond à "... doit être déclarée avant son utilisation dans l'espace de nom N ...". Il y a en fait un operator<< dans l'espace de noms actuel ( B ), de sorte qu'il l'ajoute à la liste des correspondances. Il n'y a plus de correspondance dans B , et comme la portée d'un même espace de noms est considérée comme la meilleure proximité possible entre les correspondances, elle ne s'intéresse à aucune autre portée.

Si vous placez l'opérateur dans l'espace de noms A, c'est parce que l'élément en cours d'impression est membre de A que cet espace de noms est en fait considéré, car il est inclus dans les espaces de noms de l'expression. Étant donné que l'espace de noms A est considéré, il trouve la correspondance appropriée dans cet espace de noms et se compile correctement.

Maintenant qu'il dispose d'une liste d'opérateurs possibles, il essaie de résoudre les surcharges. Malheureusement, celui trouvé dans l'espace de noms B est le seul qu'il considère et il ne correspond pas aux arguments requis.

En règle générale, les opérateurs d'insertion doivent figurer dans le même espace de noms que la classe sur laquelle il opère.





operator-keyword