6 votes

Erreur Clang : conversion ambiguë pour static_cast

J'ai le morceau de code suivant :

typedef int AliasB;
typedef unsigned short AliasA;

class Alias
{
public:
    explicit Alias(int someInt) { }

};

// (*) !! below breaks the conversion path via AliasA !!
//typedef Alias AliasA;

class C
{
public:
    C() { }
};

class B
{
public:
    B() { }
    B(const AliasB& value) { }

    operator AliasB() const
    {
        return -1000;
    }

    C combine(const B& someB) 
    {
        return C(); 
    }
};

class A
{
public:
    A() { }

    operator B() const
    {
         return B();
    }

    operator AliasA() const
    {
        return 1001;
        // (*) !! below breaks the conversion path via AliasA !!
        //return AliasA(1000);
    }

    A high() 
    {
        return A(); 
    }
    A low() 
    {
        return A(); 
    }

    C process()
    {
        return (static_cast<B>(low())).combine(static_cast<B>(high()));

        // (**) !! the below compiles fine !! 
        //B theB = low();
        //return theB.combine(high());
    }
};

inline int someFunc(unsigned int someParam, const B& bParam)
{
    return 1;
}

inline A createSomeA()
{
    return A();
}

int main ()
{
    A someA;
    unsigned int counter = 200;
    someFunc(counter, someA);
    //someFunc(counter, static_cast<B>(createSomeA()));
    someA.process();
    return 0;
}

Clang signale l'erreur suivante :

clang_static_test.cpp:66:17: error: ambiguous conversion for static_cast from 'A' to 'B'
        return (static_cast<B>(low())).combine(static_cast<B>(high()));
                ^~~~~~~~~~~~~~~~~~~~~
clang_static_test.cpp:21:7: note: candidate is the implicit copy constructor
class B
      ^
clang_static_test.cpp:25:5: note: candidate constructor
    B(const AliasB& value) { }
    ^
clang_static_test.cpp:66:48: error: ambiguous conversion for static_cast from 'A' to 'B'
        return (static_cast<B>(low())).combine(static_cast<B>(high()));
                                               ^~~~~~~~~~~~~~~~~~~~~~
clang_static_test.cpp:21:7: note: candidate is the implicit copy constructor
class B
      ^
clang_static_test.cpp:25:5: note: candidate constructor
    B(const AliasB& value) { }
    ^
2 errors generated.

Je n'arrive pas à comprendre pourquoi le compilateur génère une erreur alors que j'ai le fichier soit défini et que je rende la conversion explicite à cet endroit précis en utilisant static_cast<>. Le code passe la compilation avec les compilateurs GCC 4.5.2 et Visual Studio 2008. La version de Clang est la 3.1, construite par moi-même à partir des dépôts git de Clang et LLVM il y a quelques jours.

Clang signale-t-il une erreur réelle ? Et si oui, pourquoi est-ce une erreur car ce n'est pas pas évident pour moi (je ne demanderai pas pourquoi les autres compilateurs sont silencieux à ce sujet) ?

UPDATE : le code d'exemple est maintenant un petit exemple compilable (désolé de ne pas l'avoir fait la première fois) et reproduit la situation réelle que j'ai. Il semble que l'opérateur de conversion vers AliasA soit le problème, car s'il est supprimé, tout se compile correctement. Le problème actuel est que pour le morceau de code ci-dessus, j'obtiens également des erreurs de la part de GCC.

MISE À JOUR 2 : J'ai ajouté du code à l'exemple pour mieux refléter ma situation réelle ; la seule différence est que pour l'exemple ci-dessus, j'obtiens également une erreur de GCC, alors que pour mon code réel, ce n'est pas le cas.

3voto

Stephan Points 2496

Il y a deux façons de static_cast pour transformer un A en B :

  1. utiliser A::operator B et appeler le constructeur implicite de copie B::B(const B& value) pour créer le nouveau B

  2. utiliser A::operator AliasA , convertir le résultat en AliasB et appeler B::B(const AliasB& value)

Le compilateur ne sait pas quelle voie privilégier. Vous pouvez donner un indice pour utiliser la deuxième option en écrivant :

    someFunc(counter, static_cast<AliasA>(someA));

Pour utiliser la première option, vous pouvez omettre le casting en écrivant simplement :

    someFunc(counter, someA);

Je ne suis pas sûr que ce dernier comportement soit bien défini, mais il fonctionne au moins avec gcc et msvc.

3voto

A Fog Points 614

J'ai déposé un rapport de bogue à ce sujet auprès de clang. Ils soutiennent que ce n'est pas un bogue. Le standard C++ manque de spécification pour ce cas et donc le compilateur le signale comme ambigu. Voir rapport de bogue .

Je pense que ce comportement est contre-intuitif. Le premier chemin à travers A::operator B() est une correspondance parfaite, alors que le second chemin implique trois conversions de type. La seule chose logique à faire est de considérer la correspondance parfaite comme supérieure.

Que doit faire un compilateur lorsqu'un cas n'est pas explicitement résolu dans la norme C++ ?

  1. Produire un message d'erreur
  2. Faire un choix logique par analogie avec d'autres règles
  3. Contactez le comité de normalisation du C++ et demandez-lui de réviser la norme.

?

0voto

celavek Points 2107

Je pense avoir compris en partie ce qui se passe, mais je ne pense pas avoir compris l'ensemble de la situation. Donc les chemins de conversions que le compilateur voit ressemblent à ce qui suit :

              /--------  A  --------\
              |                     |
              |                     |
              B             AliasA(unsigned short)
              |                     |
              |                     |
      copy ctor of B            AliasB(int)
                                    |
                                    |
                           ctor B(const AliasB& value)

C'est donc logique et le compilateur a raison de signaler l'ambiguïté (J'adore les messages d'erreur et d'avertissement du clan). Une façon de le faire fonctionner est de casser le chemin de conversion via l'AliasA (voir les commentaires marqués d'un (*) dans l'exemple de code de la question). Cela fonctionne également si je casse l'expression incriminée et que je me fie uniquement à la conversion implicite, sans essayer d'expliciter static_cast<> quoi que ce soit (voir les commentaires marqués d'un astérisque (**) dans l'exemple de code de la question). Mais je ne comprends pas tout à fait ce qui se passe dans les coulisses. Je ne comprends toujours pas pourquoi il se comporte différemment dans l'application (**) parce que les chemins de conversion semblent être les mêmes, au moins pour le "theB.combine(high())" partie.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X