2009-07-12 6 views
0

Ce morceau de code compile et fonctionne comme prévu sur GCC 3.x et 4.x:MSVC: Union vs. classe/struct avec les opérateurs ami inline

#include <stdio.h> 

typedef union buggedUnion 
{ 
public: 
      // 4 var init constructor 
     inline buggedUnion(int _i) { 
      i = _i; 
     } 

     friend inline const buggedUnion operator - (int A, const buggedUnion &B) { 
      return buggedUnion(A - B.i); 
     } 

     friend inline const buggedUnion operator - (const buggedUnion &A, const buggedUnion &B) { 
      return buggedUnion(A.i - B.i); 
     } 

     int i; 

} buggedUnion; 

int main() 
{ 
    buggedUnion first(10); 
    buggedUnion second(5); 

    buggedUnion result = 10 - (first - second); 

    printf("%d\n", result.i); // 0 

    return 0; 
} 

MSVC, cependant, ne compilera pas ce code , se plaignant:

main.cpp(60) : error C3767: '-': candidate function(s) not accessible 
     could be the friend function at 'main.cpp(41)' : '-' [may be found via argument-dependent lookup] 
     or the friend function at  'main.cpp(45)' : '-' [may be found via argument-dependent lookup] 
main.cpp(60) : error C2676: binary '-' : 'buggedUnion' does not define this operator or a conversion to a type acceptable to the predefined operator 

Lequel des compilateurs est correct? Comment cela peut-il être résolu? J'essaye de réaliser le code propre (aucun dehors des méthodes d'ami) tout en maintenant la portabilité, la flexibilité et le code auto-documentant.

Quelques notes:

  • Ceci est un test cas pour montrer le problème, le type de données d'origine est beaucoup plus sophistiqué et soigneusement conçu, mais ne fonctionne pas dans MSVC (compilateur principal est GCC, bien que la compatibilité MSVC soit également souhaitée). L'ajout de 'public:' au début de la déclaration d'union ne le résout pas.
  • Ajout « public: » avant chaque opérateur ne résout pas
  • Conversion du cas de test à une struct/classe ne fixe, mais ce n'est pas souhaitée (S'il vous plaît pas de flammes, je me suis raisons La plupart d'entre eux. sont les limites du langage C++)
  • méthode de l'opérateur doit être laissé à l'portée mondiale (et non une fonction membre)

solution optimale ne se baserait pas sur le déplacement de la déclaration en dehors de la définition du syndicat pour des raisons d'esthétique (plus de 24 combinaisons différentes d'opérateurs et d'opérateurs ands), mais cela sera fait s'il n'y a pas d'autre solution.

+0

Votre code peut compiler et s'exécuter correctement sur gcc (GCC) 3.4.4 en utilisant cygwin 32 bits de Win 7. –

Répondre

1

Il est difficile de dire lequel est le bon, étant donné que struct s non nommés ne sont pas autorisés par la norme (bien qu'ils soient une extension commune), et en tant que tel, le programme est mal formé.

Modifier: Cela semble être un bogue dans msvc, car le code suivant, qui est parfaitement valide, ne peut pas être compilé.

union buggedUnion 
{ 
    friend buggedUnion operator - (int A, const buggedUnion &B) { 
     return B; 
    } 

    friend buggedUnion operator - (const buggedUnion &A, const buggedUnion &B) { 
     return A; 
    } 

    int i; 
}; 


int main() 
{ 
    buggedUnion first = { 1 }; 
    buggedUnion second = { 1 }; 
    buggedUnion result = 3 - (first - second); 
} 

Vous pouvez contourner ce problème en définissant les fonctions en dehors de la classe.

union buggedUnion 
{ 
    int i; 
}; 

buggedUnion operator - (int A, const buggedUnion &B) { 
    return B; 
} 

buggedUnion operator - (const buggedUnion &A, const buggedUnion &B) { 
    return A; 
} 

Vous pouvez même conserver le statut d'ami en déclarant les fonctions à l'intérieur de la classe (mais toujours les définir en dehors), mais je doute que vous auriez jamais besoin que dans une union. Notez que j'ai supprimé les numéros inutiles typedef et inline.

+0

J'ai mis à jour le cas de test pour prouver que la structure anonyme n'a rien à voir avec ça. – LiraNuna

+0

Ce n'est pas un bogue dans Visual C++ c'est le comportement correct selon la spécification. Les fonctions Friend définies à l'intérieur d'une classe n'ont aucune visibilité en dehors de la classe et ne devraient en avoir aucune. –

+0

Codage de la roue, les fonctions amis ne sont pas membres de la classe, elles sont injectées dans la portée qui les entoure et sont donc certainement visibles. – avakar

0

Vous devez déclarer cette fonction friend dans la portée englobante, car une fois que vous les avez déclarées dans la classe, elles ne sont plus visibles dans la portée externe. Donc, soit déplacer le corps de la fonction de la classe comme avakar dit, ou les conserver dans la classe et ajoutez la ligne suivante pour réintroduire le nom dans la portée englobante:

extern const buggedUnion operator-(const buggedUnion& A, const buggedUnion&B); 

int main() 
{ 
    ...etc 

Hope this helps.Je ne sais pas si c'est un bug mais il me semble (?) Être un comportement correct, maintenant implémenté correctement, que beaucoup de compilateurs utilisaient pour interpréter différemment. Voir: - injection libre dans http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Dialect-Options.html.

+0

Alors j'ai regardé ça. Vous avez raison en définissant la fonction à l'intérieur de la classe, vous l'avez retiré de la recherche normale (c'est 11.4/5). Mais la recherche d'ADL n'est pas affectée par ceci. Je crois toujours que c'est un bug MSVC, d'autant plus que le même code fonctionne avec 'struct'. – avakar

+0

Eh bien, il semble que je suis en infériorité numérique. Heureusement, c'est la première priorité sur la pile de développement MS, donc si c'est un bug, nous pouvons tous nous reposer en sachant qu'il sera corrigé dans quelques jours ... :) –

+0

On ne sait jamais, si le problème persiste le bêta de 10, et quelqu'un le soulève, ils pourraient faire quelque chose à ce sujet. Je ne connais pas la politique interne ni les priorités de l'équipe MSVC, mais en général, dans toute entreprise, cibler les rapports de bogues à la version bêta signifie qu'il y a plus de chances que quelqu'un veuille bien modifier la base de code ... –

0

Le code suivant compile correctement dans Visual C++ 2008:

union buggedUnion 
{ 
    int i; 

    friend buggedUnion operator - (int A, const buggedUnion &B); 
    friend buggedUnion operator - (const buggedUnion &A, const buggedUnion &B); 
}; 

buggedUnion operator - (int A, const buggedUnion &B) 
{ 
    return B; 
} 

buggedUnion operator - (const buggedUnion &A, const buggedUnion &B) 
{ 
    return A; 
} 

Bien que la documentation MSDN indique que l'écriture la définition de la fonction ami dans une classe met effectivement la fonction dans la portée du fichier, cela semble ne pas fonctionner pour les syndicats. Comme cela fonctionne pour la classe et la structure, je pense que cela peut être un bug. Puisque la mise en œuvre que j'ai donnée ci-dessus devrait fonctionner sur GCC, je pense que cela peut être considéré comme une solution de contournement appropriée et le bug ne sera probablement pas corrigé dans MSVC.