Question

Je viens contre une énigme frustrante dans ma base de code. Je ne peux pas tout à fait dire pourquoi mon code génère cette erreur, mais (par exemple) std :: string ne fonctionne pas.

class String {
public:
    String(const char*str);
    friend String operator+ ( const String& lval, const char *rval );
    friend String operator+ ( const char *lval, const String& rval );
    String operator+ ( const String& rval );
};

La mise en œuvre de ces derniers est assez facile d'imaginer vous-même.

Mon programme pilote contient les éléments suivants:

String result, lval("left side "), rval("of string");
char lv[] = "right side ", rv[] = "of string";
result = lv + rval;
printf(result);
result = (lval + rv);
printf(result);

Ce qui génère l'erreur suivante dans gcc 4.1.2:

driver.cpp:25: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
String.h:22: note: candidate 1: String operator+(const String&, const char*)
String.h:24: note: candidate 2: String String::operator+(const String&)

Jusqu'à présent si bon, non? Malheureusement, mon chaîne constructeur (const char * str) est si pratique d'avoir comme un constructeur implicite, en utilisant le mot-clé qui explicite pour résoudre cela déclenchait un tas de problèmes différents.

De plus ... std :: string ne pas avoir recours à cela, et je ne peux pas comprendre pourquoi. Par exemple, dans basic_string.h, ils sont déclarés comme suit:

template<typename _CharT, typename _Traits, typename _Alloc>
basic_string<_CharT, _Traits, _Alloc>
operator+(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
          const basic_string<_CharT, _Traits, _Alloc>& __rhs)

template<typename _CharT, typename _Traits, typename _Alloc>
basic_string<_CharT,_Traits,_Alloc>
operator+(const _CharT* __lhs,
          const basic_string<_CharT,_Traits,_Alloc>& __rhs);

et ainsi de suite. Le constructeur basic_string n'est pas déclaré explicitement. Comment cela provoque pas la même erreur que je reçois, et comment puis-je obtenir le même comportement ??

Était-ce utile?

La solution

La raison de l'ambiguïté est qu'une fonction candidate est meilleure qu'une autre fonction candidate que si pas de ses paramètres sont plus mal que les paramètres correspondance de l'autre. Tenez compte de vos deux fonctions:

friend String operator+(const String&, const char*); // (a)
String operator+(const String&);                     // (b)

Vous appelez operator+ avec un String et un const char*.

Le second argument, de type const char*, correspond clairement (a) mieux que (b). Il est une correspondance exacte pour (a), mais est nécessaire pour la conversion définie par l'utilisateur (b).

Par conséquent, afin qu'il y ait une ambiguïté, le premier argument doit correspondre à (b) mieux que (a).

Le String sur le côté gauche de l'appel à operator+ n'est pas const. , Elle correspond donc (b), qui est une fonction membre non-const, mieux que (a), qui prend const String&.

Par conséquent, l'une des solutions suivantes lèverait l'ambiguïté:

  • Changer le operator+ membre d'être une fonction membre const
  • Modifier la operator+ non-membre de prendre une String& au lieu d'un const String&
  • Appel operator+ avec une chaîne const sur le côté gauche

De toute évidence, le premier, également suggéré par Unclebens , est le meilleur chemin à parcourir.

Autres conseils

Il suffit dans ce cas juste pour définir le operator+:

String operator+(const String& lval, const String& rval);

Parce que vous fournissez un constructeur de prendre un char*, un String peut être construit à partir d'un char* lors de l'appel à operator+. Par exemple:

String hello = "Hello, ";
const char* world = "world!";

String helloWorld = hello + world;

Un String temporaire sera construit avec le contenu du char* world (parce que votre constructeur est pas explicite), puis seront transmis les deux objets String à operator+.

L'erreur disparaît si vous déclarez le membre + const comme il devrait être.

class String {
public:
    String(const char*str);
    friend String operator+ ( const String& lval, const char *rval );
    friend String operator+ ( const char *lval, const String& rval );
    String operator+ ( const String& rval ) const; //<-- here
};

Je ne sais pas quelle est la raison, cependant. Peut-être qu'il préfère les arguments liant à la référence const si possible, de sorte que la première surcharge est une meilleure adéquation de la valeur à la main gauche et la troisième surcharge a une meilleure adéquation de la valeur de droite.

Une meilleure explication. (Doit avoir mal interprété le problème un peu.)


printf(result);

Ne me dites pas votre chaîne a la conversion implicite const char* ... C'est le mal.

Modèle et fonctions non-modèle suivent des règles différentes. Les fonctions de modèle sont sélectionnés sur les types de paramètres réels, sans aucune conversion appliquées. Dans le cas du non-modèle (par exemple votre code) une conversion implicite peut être appliquée. Ainsi les choses dans basic_string est basé sur un modèle non ambigu, mais le vôtre est.

Vous avez montré que basic_string a des implémentations de operator+ correspondant aux deuxième et troisième opérateurs dans votre String de classe. Est-ce que basic_string ont également un opérateur correspondant à votre premier opérateur [friend String operator+ ( const String& lval, const char *rval );]?

Qu'est-ce qui se passe si vous supprimez cet opérateur?

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