Impossible pour le compilateur C ++ de trouver une fonction (liée à un espace de noms)

StackOverflow https://stackoverflow.com/questions/493985

Question

Je travaille dans Visual Studio 2008 sur une tâche de programmation C ++. Nous avions des fichiers qui définissent la hiérarchie de l'espace de noms suivant (&, & Les noms sont juste pour le plaisir de ce post, je sais quot namespace XYZ-namespace quot; est redondant):

(MAIN-NAMESPACE){

      a bunch of functions/classes I need to implement...

      (EXCEPTIONS-NAMESPACE){

            a bunch of exceptions
      }

      (POINTER-COLLECTIONS-NAMESPACE){

            Set and LinkedList classes, plus iterators
      }
}

Le contenu de MAIN-NAMESPACE est divisé en plusieurs fichiers et, pour une raison que je ne comprends pas, l'opérateur < < pour Set et LinkedList est entièrement à l'extérieur de MAIN-NAMESPACE (mais dans le fichier d'en-tête de Set et LinkedList). Voici la version Set:

template<typename T>
std::ostream& operator<<(std::ostream& os, 
                         const MAIN-NAMESPACE::POINTER-COLLECTIONS-NAMESPACE::Set<T>& set)

Maintenant, voici le problème: j'ai la structure de données suivante:

Set A
Set B
Set C
double num

Il est défini comme appartenant à une classe de MAIN-NAMESPACE. Lorsque je crée une instance de la classe et essaie d'imprimer l'un des ensembles, cela me dit que: erreur C2679: binaire '< <' : Aucun opérateur trouvé qui prend un opérande à droite de type 'const MAIN-NAMESPACE :: POINTER-COLLECTIONS-namespace :: Set' (ou il n'y a pas de conversion acceptable)

Cependant, si je viens d'écrire une fonction main () et de créer l'ensemble A, remplissez-le et utilisez l'opérateur - cela fonctionne.

Avez-vous une idée du problème? (note: j'ai essayé n'importe quelle combinaison d'utilisation et d'inclusion que je pouvais penser).

Était-ce utile?

La solution 2

OK, j'ai compris ça. L'intuition de Jpalecek sur l'existence d'un autre opérateur < < dans l’espace de nommage était correct (apparemment j’ai oublié de commenter).

Les règles de recherche pour les espaces de noms démarrent d'abord la recherche dans l'espace de noms de l'appel de fonction et recherchent les espaces de noms qui les entourent, jusqu'à l'espace de noms global (puis effectue la recherche dépendante de l'argument si aucune correspondance n'est trouvée). Toutefois, si, le long du chemin, il trouve une correspondance pour l'opérateur & Lt; & Lt ;,, il interrompt la recherche, même si les types utilisés dans ces fonctions peuvent être incompatibles, comme c'était le cas ici.

La solution consiste à l'inclure dans le MAIN-NAMESPACE (ce que je ne suis pas autorisé à faire) ou à l'importer de l'espace de noms global avec & "à l'aide de: opérateur < < " ;.

Autres conseils

Étrange - bien que placer des fonctions libres associées à un type dans un espace de noms différent soit une pratique inappropriée, les déclarations d'espace de noms global sont toujours visibles.

La seule chose à laquelle je peux penser, c’est que la déclaration portant le même nom dans MAIN-NAMESPACE masquerait celle de l’espace de noms global - n’existe-t-il pas un operator<<, éventuellement pour un type totalement non lié, dans using ::operator<<? Si tel est le cas, vous devriez résoudre ce problème par <=> la déclaration dans <=>. Exemple:

namespace A
{
namespace B
{
  class C{};
}

}

void f(A::B::C*);

namespace A
{
  void f(int*); // try commenting
  using ::f; // these two lines
  void g()
  {
    B::C* c;
    f(c);
  }
}

Essayez d’appeler explicitement la fonction?

::operator<<( cout, myObj );

Comme l'a souligné SoaBox, essayez de l'appeler explicitement.

Pour votre information, si vous souhaitez appeler une fonction globale qui a été masquée dans l'espace de noms actuel, faites précéder la fonction par :: pour contourner la fonction locale et appeler la fonction globale.

  

Essayez d’appeler explicitement la fonction?

     

:: operator < < (cout, myObj);

Oui, ça marche!

  

il va essayer de trouver la fonction f dans   le nom actuel (à la place de   call) ou dans les espaces de noms suivants   des types c1 et c2 (namespace1,   namespace2 :: namespace3), mais ce sera   n'essayez pas d'autres espaces de noms dans le   rechercher.

Voyons donc si j'ai raison: la raison invoquant l'opérateur < < depuis une fonction main (), c’est parce que j’étais dans l’espace de noms global (tout comme l’opérateur < <). La raison de son échec lors de l'appel de la classe implémentée est que la classe se trouvait dans un espace de noms non global et qu'aucune variable ne dirigeait le compilateur vers l'espace de noms global.

OK, les gens ont demandé des exemples spécifiques, voici donc la partie pertinente du code. // Disclamer: dans le mince cas, quelqu'un de mon université voit cela, le rencontre dans le fichier de soumission et décide de le copier ou quelque chose d'autre, mon numéro d'étudiant est 311670137

Ceci est le fichier d'en-tête Set.h:

   namespace MTM {//This is the MAIN-NAMESPACE

    namespace PointerCollections {

        (ITERATORS AND PREDICATE CLASSES)

        template<typename T>
        class Set {
        public:

        /////////////////////////////////
        // Definitions
        /////////////////////////////////

        private:

        /////////////////////////////////
        // Definitions
        /////////////////////////////////

        };


///////////////////////////////////////////////////////////////////////////////
// The implementation part. 
///////////////////////////////////////////////////////////////////////////////
      }
}
// operator<< - the same a Set::print(std::ostream& os,
//                                    const BinaryPredicate<T>& predicate) 
// function called with the 'predicate' parameter omitted
template<typename T>
std::ostream& operator<<(std::ostream& os, 
                         const MTM::PointerCollections::Set<T>& set){

    set.print(os);
    return os;
}

Voici ce que j'ai défini dans un autre fichier:

namespace MTM {
    using std::ostream;

    class Schedule {
    public:
      ///////////////////
      //Definitions, including: 
      ///////////////////
    void registerStation(string stationName);
    void reportRegisteredStations(std::ostream& outputStream) const;

    private:     //My database
               //All the classes Set recieves are defined elsewhere
        Set<RegisteredStation> places;
        Set<BusLine> busses;
        Set<TrainLine> trains;
        double tarifForBuses;
        double tarifForTrains;
    };

}

Et voici le texte principal:

Schedule s();
s.registerStation("1");
s.reportRegisteredStations(cout);//This invokes the error. Definition follows:

reportRegisteredStations est défini comme:

void Schedule::reportRegisteredStations(std::ostream& outputStream) const{

        outputStream<<places;
    }

Cela fonctionne pour moi

#include <iostream>
#include <string>
using std::string;

   namespace MTM {//This is the MAIN-NAMESPACE

    namespace PointerCollections {

        template<typename T>
        class Set {
        };


      }
}
template<typename T>
std::ostream& operator<<(std::ostream& os, 
                         const MTM::PointerCollections::Set<T>& set){

    return os;
}

namespace MTM {
    using std::ostream;
  using PointerCollections::Set;
    class Schedule {
    public:
      ///////////////////
      //Definitions, including: 
      ///////////////////
    void registerStation(string stationName);
    void reportRegisteredStations(std::ostream& outputStream) const;

    private:     //My database
               //All the classes Set recieves are defined elsewhere
        Set<int> places;
        Set<int> busses;
        Set<int> trains;
        double tarifForBuses;
        double tarifForTrains;
    };
void Schedule::reportRegisteredStations(std::ostream& outputStream) const{

        outputStream<<places;
    }

}

int main()
{
  MTM::Schedule s;
  s.reportRegisteredStations(std::cout);
}

CORRECTION : le texte ci-dessous est basé sur l'expérience acquise avec la famille de compilateurs g ++. Après le commentaire de la réponse, j'ai relu le standard (qui stipule que ADL sera utilisé par rapport à la recherche de nom classique, recherche qui devrait trouver l'opérateur & Lt; & Lt;). J'ai également essayé avec le compilateur comeau (le compilateur conforme le plus standard que je connaisse) et le symbole est trouvé. Cela semble être un problème avec g ++ (versions testées 3.3, 4.1, 4.3).

Réponse originale:

Recherchez la recherche Koening (techniquement, ADL: recherche dépendante de l’argument).

La réponse courte est que si vous avez la classe suivante:

namespace test {
    class A {};
}

l'opérateur d'insertion de flux doit être défini comme suit:

namespace test {
    std::ostream& operator<<( std::ostream&, A const & );
}

Les fonctions ou les opérateurs doivent être définis dans le même espace de noms que l'un des arguments utilisés. (*)

Lorsque le compilateur trouve un appel de fonction tel que:

namespace test2 {
   void g() {
      namespace1::class1 c1;
      namespace2::namespace3::class2 c2;
      f( c1, c2 );
   }
}

il essaiera de trouver la fonction f dans l’espace de nom actuel (à l’endroit de l’appel) ou dans les espaces de nommage suivants de types c1 et c2 (namespace1, namespace2 :: namespace3) n'essaiera pas d'autres espaces de noms dans la recherche.

(*) Dans ce cas, vous êtes plutôt limité à l'espace de noms test , car vous n'êtes pas autorisé à ajouter une fonction à l'espace de noms std (uniquement les spécialisations de modèles).

Fin du message d'origine .

Même si, comme indiqué précédemment, cela pourrait être un problème avec le compilateur, il est courant d'utiliser toutes les fonctions libres qui fonctionnent sur un type défini par l'utilisateur dans le même espace de noms que le type lui-même.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top