2009-08-07 8 views
2

Lors du portage du code Windows sur Linux, j'ai rencontré le message d'erreur suivant avec GCC 4.2.3. (Oui, je suis conscient du fait que c'est un peu ancienne version, mais je ne peux pas facilement mettre à jour.)Surcharge de l'opérateur C++ - diffusion à partir de la classe

main.cpp:16: error: call of overloaded ‘list(MyClass&)’ is ambiguous 
/usr/include/c++/4.2/bits/stl_list.h:495: note: candidates are: std::list<_Tp, _Alloc>::list(const std::list<_Tp, _Alloc>&) [with _Tp = unsigned char, _Alloc = std::allocator<unsigned char>] 
/usr/include/c++/4.2/bits/stl_list.h:484: note:     std::list<_Tp, _Alloc>::list(size_t, const _Tp&, const _Alloc&) [with _Tp = unsigned char, _Alloc = std::allocator<unsigned char>] 

J'utilise le code suivant pour générer cette erreur.

#include <list> 
class MyClass 
    { 
    public: 
     MyClass(){} 

     operator std::list<unsigned char>() const { std::list<unsigned char> a; return a; } 
     operator unsigned char() const { unsigned char a; return a; } 

    }; 

    int main() 
    { 
     MyClass a; 
     std::list<unsigned char> b = (std::list<unsigned char>)a; 

     return 0; 
    } 

Est-ce que quelqu'un a rencontré cette erreur? Plus important encore, comment le contourner? (Il est possible d'éviter complètement la surcharge, bien sûr, en utilisant des fonctions telles que GetChar(), GetList() etc, mais je voudrais éviter cela.)

(Soit dit en passant, la suppression « operator unsigned char() » supprime l'erreur.)

Répondre

1

Il compile correctement si vous supprimez la distribution, et j'ai vérifié que l'opérateur std :: list est en cours d'exécution.

int main() 
{ 
    MyClass a; 
    std::list<unsigned char> b = a; 

    return 0; 
} 

Ou si vous l'avez converti en une référence const.

int main() 
    { 
     MyClass a; 
     std::list<unsigned char> b = (const std::list<unsigned char>&)a; 

     return 0; 
    } 
+0

Pourquoi est-ce le cas? Je sais pertinemment que vous pouvez implicitement appeler un casting surchargé. –

+0

Désolé, appelez explicitement. –

+0

Pas vraiment sûr, je suppose que si vous ne dites pas explicitement que vous voulez utiliser votre opérateur (en cast), il peut utiliser d'abord l'opérateur standard à const et puis le vôtre, mais quand vous voulez explicitement utiliser le direct des conflits avec le const &, mais pas vraiment sûr. –

6

L'ambiguïté vient de l'interprétation de l'expression coulé.

Lors du choix de la conversion, le compilateur premier considère un casting de style static_cast et considère comment résoudre une initialisation qui ressemble à ceci:

std::list<unsigned_char> tmp(a); 

Cette construction est ambiguë a a une conversion définie par l'utilisateur à un std::list<unsigned char> et à un unsigned char et std::list<unsigned char> a à la fois un constructeur qui prend un const std::list<unsigned char>& et un constructeur qui prend size_t (auquel un unsigned char peut être promu).

Lors de la coulée à une const std::list<unsigned_char>&, cette initialisation est considéré:

const std::list<unsigned_char>& tmp(a); 

Dans ce cas, lorsque la conversion défini par l'utilisateur à std::list<unsigned_char> est choisie, la nouvelle référence peut se lier directement à la suite de la conversion. Si la conversion définie par l'utilisateur à unsigned char est choisie, un objet temporaire de type std::list<unsigned char> devra être créé, ce qui rend cette option une séquence de conversion pire que l'option précédente.

2

J'ai simplifié votre exemple à ce qui suit:

typedef unsigned int size_t; 

template <typename T> 
class List 
{ 
public: 
    typedef size_t size_type; 
    List (List const &); 
    List (size_type i, T const & = T()); 
}; 

typedef List<unsigned char> UCList; 

class MyClass 
{ 
public: 
    operator UCList const() const; 
    operator unsigned char() const; 
}; 

void foo() 
{ 
    MyClass mc; 
    (UCList)mc; 
} 

Le premier point, est que la norme définit que la fonte de style C devrait utiliser la distribution de style plus approprié C++, et dans ce cas qui est static_cast.Ainsi, la distribution est équivalente à:

static_cast<UCList> (mc); 

La définition de static_cast dit:

Une expression e peut être converti explicitement à un type T en utilisant un static_cast du formulaire static_cast<T>(e) si la déclaration "T t(e);" est bien formée, pour une variable temporaire inventé t (8,5)

ainsi la sémantique pour la coulée sont les mêmes que pour:

UCList tmp (mc); 

De 13.3.1.3 nous obtenons l'ensemble des constructeurs de candidats que nous pouvons utiliser dans UCList:

UCList (UCList const &)    #1 
UCList (size_type, T const & = T()); #2 

Que se passe ensuite est à deux pas de résolution de surcharge, une pour chaque opérateur de conversion.

Conversion en # 1: Avec un type de cible de UCList const &, la résolution de surcharge sélectionne entre les opérateurs de conversion suivants .: "operator UCList const()" et "operator unsigned char()". L'utilisation de unsigned char nécessiterait une conversion utilisateur supplémentaire et n'est donc pas une fonction viable pour cette étape de surcharge. Par conséquent, la résolution de surcharge réussit et utilisera operator UCList const().

Conversion en # 2: Avec le type de cible size_t. L'argument par défaut ne participe pas à la résolution de surcharge. La résolution de surcharge sélectionne à nouveau entre les opérateurs de conversion: "operator UCList const()" et "operator unsigned char()". Cette fois, il n'y a pas de conversion de UCList à unsigned int et donc ce n'est pas une fonction viable. Un unsigned char peut être promu à size_t et donc cette résolution de surcharge de temps réussit et utilisera "operator UCList const()". Mais maintenant, au niveau supérieur, il existe deux étapes de résolution de surcharge séparées et indépendantes qui ont été converties avec succès de mc à UCList. Le résultat est donc ambigu. Pour expliquer ce dernier point, cet exemple est différent du cas de résolution de surcharge normal. Normalement, il y a un 1: la relation entre les n types d'arguments et paramètres:

void foo (char); 
void foo (short); 
void foo (int); 

void bar() { 
    int i; 
    foo (i); 
} 

Ici il y a i=>char, i=>short et i=>int. Ceux-ci sont comparés par résolution de surcharge et la surcharge int est sélectionnée.

Dans le cas ci-dessus, nous avons une relation m: n. La norme décrit les règles à sélectionner pour chaque argument individuel et tous les paramètres «n», mais c'est là qu'elle se termine, elle ne spécifie pas comment nous devrions décider d'utiliser les différents arguments «m».

Espérons que cela a du sens!

MISE À JOUR:

Les deux types de syntaxe d'initialisation ici sont:

UCList t1 (mc); 
UCList t2 = mc; 

't1' est une initialiation directe (13.3.1.3) et tous constructeurs sont inclus dans la surcharge ensemble. C'est presque comme avoir plus d'une conversion définie par l'utilisateur. Il y a l'ensemble des constructeurs et l'ensemble des opérateurs de conversion. (c'est-à-dire m: n).

Dans le cas de « t2 » utilise la syntaxe copy-initialisation (13.3.1.4) et les règles différent:

Dans les conditions spécifiées dans 8,5, dans le cadre d'une copie de l'initialisation d'un objet de type classe, une conversion définie par l'utilisateur peut être appelée pour convertir une expression d'initialisation en type d'objet en cours d'initialisation. résolution de surcharge est utilisé pour sélectionner la conversion définie par l'utilisateur à invoquer

Dans ce cas, il est juste un à taper, UCList, et donc il n'y a que l'ensemble de l'opérateur de conversion Surcharges à considérer, par exemple. nous ne considérons pas les autres constructeurs de UCList.

+0

Je me demandais simplement, apparemment la construction et l'assignation de copie directe à un nouvel objet (construction de copie aussi) ne fonctionnent pas avec les mêmes règles? UCList t (mc); #Ceci est ambigu UCList mylist = mc; #Ceci n'est pas ambigu –

+0

Réponse extraordinaire. Vous méritez d'être voté beaucoup de fois :) –

+0

"_Hope cela a du sens! _" Cela n'a aucun sens, jusqu'à ce que l'on se rende compte que '(T) x' est la même chose que' T (x) '! C'est juste une erreur de conception précoce. – curiousguy

Questions connexes