2017-01-12 8 views
2

J'ai rencontré une erreur étrange concernant la recherche de nom en C++.C++ Erreur de recherche de nom de fonction surchargée

L'erreur peut être recréée en utilisant l'exemple minimale suivante:

#include <vector> 
#include <iostream> 

std::ostream& operator<<(std::ostream& out, const std::vector<int>& a) { 
    for (size_t i = 0; i < a.size(); i++) { 
     out << a[i] << std::endl; 
    } 
    return out; 
} 

namespace Test { 

    struct A { 
     // Label 1 
     friend std::ostream& operator<<(std::ostream& out, const A&) { 
      return out << "A" << std::endl; 
     } 
    }; 

    struct B { 
     void printVector() noexcept { 
      std::vector<int> v{1, 2, 3}; 
      std::cout << v << std::endl; // The error occurs in this line 
     } 
     // Label 2 
     friend std::ostream& operator<<(std::ostream& out, const B&) { 
      return out << "B" << std::endl; 
     } 
    }; 

} 

int main() { 
    Test::B().printVector(); 
} 

Compiler cela va entraîner dans le message d'erreur suivant:

cannot bind 'std::ostream {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&' 

Vous pouvez tester par vous-même ici: http://cpp.sh/5oya

La partie étrange est, que le code compile et fonctionne bien si vous supprimez l'une des fonctions étiquetées avec // Label 1 respectivement // Label 2.

Ma question est maintenant: Que se passe-t-il ici? Comment peut-il être réparé?

+0

[Fonctionne ici] (http://coliru.stacked-crooked.com/a/99865250debbfc32) – NathanOliver

+0

Visual Studio 2015 génère ce message d'erreur: "erreur C2679: binaire '<<': aucun opérateur trouvé qui prend un droit -opérande de type 'std :: vector >' (ou il n'y a pas de conversion acceptable) " –

+0

g ++ version 5.3.1 produit ce message d'erreur: 'Erreur: aucune correspondance pour l'opérateur << << (les types d'opérandes sont» std :: ostream {aka std :: basic_ostream } «et» std :: vector «)' – zuenni

Répondre

0

Autre solution de contournement sera surcharger l'opérateur < < l'intérieur de l'espace de noms std, au lieu de l'espace de noms global (La recherche va trouver à portée d'espace de noms)

namespace std 
{ 
    std::ostream& operator<<(std::ostream& out, const std::vector<int>& a) { 
     for (size_t i = 0; i < a.size(); i++) { 
      out << a[i] << std::endl; 
     } 
     return out; 
    } 
} 

Essayez Here.

[edited]

Une autre solution, et pour les puritains qui ne veulent pas polluer l'espace de noms global, ou std, est à

...have the insertion operators in the same namespace as the class upon which it operates.

... comme discuté here.

namespace Test 
{ 
    std::ostream& operator<<(std::ostream& out, const std::vector<int>& a) { 
     for (size_t i = 0; i < a.size(); i++) { 
      out << a[i] << std::endl; 
     } 
     return out; 
    } 
} 

Code de travail here.

+0

Je n'aime pas que cela nécessite d'étendre l'espace de noms std. Mais l'utilisation de ce travail ne nécessite pas de changer le code appelant, donc je vais utiliser cette solution. Merci beaucoup. – zuenni

+0

@Rama, Cette solution invoque en fait un comportement indéfini, par conséquent * * ne fonctionne pas ** Vous pouvez spécialiser n'importe quel modèle dans l'espace de noms 'std ::', mais vous n'êtes pas autorisé à surcharger n'importe quelle fonction 'std :: La norme ISO C++ [invoque un comportement indéfini à tout programme C++ qui ajoute quelque chose à] (http://eel.is/c++draft/namespace.constraints#namespace.std-1) le 'std ::' namespace – WhiZTiM

+0

@WhiZTiM Ohh !! donc vous pensez que faire nu à l'échelle mondiale, il est plus étendu que dans un espace de noms particulier? – Rama

0

Vous rencontrez des problèmes d'ADL, même si je pense que la recherche de noms devrait avoir trouvé votre surcharge dans l'espace de noms global. (Je n'ai pas de guillemets standard C++ spécifiques pour savoir si le compilateur est encore faux). GCC (version 6.3) et compilateurs Clang (version 3.8.0) trouve la surcharge vu here

Une solution consiste à importer le name de l'opérateur global dans votre espace de noms actuel:

std::vector<int> v{1, 2, 3}; 
using ::operator <<; 
std::cout << v << std::endl; 

Comme on le voit ici: http://cpp.sh/95kuq

Un autre travail autour est d'appeler explicitement la surcharge globale comme:

std::vector<int> v{1, 2, 3}; 
::operator << (std::cout, v); 
std::cout << std::endl; 

Comme on le voit ici: http://cpp.sh/4btyq

+0

Oui, cela fonctionne, mais il supprime la beauté de l'utilisation des opérateurs surchargés. – zuenni

+0

@zenni, eh bien, vous pouvez simplement importer l'opérateur global comme 'using :: operator <<;' comme vu [ici] (http://cpp.sh/95kuq). J'ai révisé ma réponse pour refléter cela – WhiZTiM

+0

Merci pour vos solutions. Les deux fonctionnent effectivement. Mais je préfère une solution, où le code appelant peut être maintenu inchangé et ressemble à ce que l'on attendrait de lui. – zuenni