Question

Comme exercice d'apprentissage, j'ai étudié le fonctionnement de la conversion de type automatique en C ++. Je sais que la conversion automatique de types doit généralement être évitée, mais j'aimerais approfondir mes connaissances en C ++ en comprenant tout de même son fonctionnement.

J'ai créé une classe StdStringConverter pouvant être automatiquement convertie en std::string, mais le compilateur (g ++ 4.3.4 sous Debian) ne semble pas effectuer la conversion lorsque l'objet est comparé à un réel CStringConverter (ignorez le manque de références par passe et de création inutile d'objets temporaires):

#include <string>

class StdStringConverter
{
public:
    explicit StdStringConverter(std::string name) : m_name(name) {}
    operator const std::string () const { return m_name; }
private:
    std::string m_name;
};

int main()
{
    StdStringConverter converter(std::string("Me"));
    const std::string name = "Me";
    // Next line causes compiler error:
    // no match for 'operator==' in 'converter == name'
    return (converter == name) ? 0 : 1;
}

Par contre, si je le change légèrement en classe char, la conversion automatique a lieu , bien que la comparaison des pointeurs char* ne soit probablement pas ce que je souhaitais:

#include <string>

class CStringConverter
{
public:
    explicit CStringConverter(std::string name) : m_name(name) {}
    operator const char* () const { return m_name.c_str(); }
private:
    std::string m_name;
};

int main()
{
    CStringConverter converter(std::string("Me"));
    const char* name = "Me";
    // Next line compiles fine, but they are not equal because the
    // pointers don't match.
    return (converter == name) ? 0 : 1;
}

Y at-il quelque chose de spécial dans la différence entre un <=> et un <=> dans ce contexte qui empêche le compilateur de les traiter de la même manière?

Était-ce utile?

La solution

Le problème est dû au fait que std :: string est en fait une instance du modèle de classe std :: basic_string. Un opérateur == disponible dans l'espace de noms std utilise deux modèles std :: basic_string:


template<class charT, class traits, class Allocator>
bool operator==(const basic_string& lhs,
                const basic_string& rhs);

Si cette version de operator == était surchargée spécifiquement sur std :: string, votre code irait bien. Mais ce n'est pas le cas, ce qui obligerait le compilateur à déduire les arguments de modèle sur les paramètres de modèle de std :: basic_string afin qu'il puisse comprendre que le retour de votre opérateur de conversion est une correspondance possible.

Cependant, le compilateur ne fera pas cela. Je ne sais pas quelle partie de la norme énonce cela avec précision. Mais l’idée générale est que de telles conversions ne fonctionnent que pour les types non modèles.

Je peux vous suggérer de placer StdStringConverter dans un espace de noms et de fournir une version de operator == pour std :: string dans cet espace de noms. Ainsi, lorsque votre compilateur trouve une expression comme celle-ci, ADL (Argument Dependent Lookup) entre en jeu et que tout fonctionne correctement.


#include <string>

namespace n1 {

class StdStringConverter
{
public:
    explicit StdStringConverter(std::string name) : m_name(name) {}
    operator std::string () { return m_name; }
private:
    std::string m_name;
};

bool operator==(std::string const& a, std::string const& b)
{
  return a == b; //EDIT: See Paul's comment on std::operator== here.
}

}

int main()
{
    using namespace n1;
    StdStringConverter converter(std::string("Me"));
    std::string name = "Me";
    return (converter == name) ? 0 : 1;   
}

Autres conseils

Dans le premier exemple, les deux classes comparées (string et StdStringConverter) ne reçoivent aucun traitement spécial du compilateur pour la conversion de type. Cela signifie que la surcharge de l'opérateur que vous avez provoquée ne se déclenche même pas. Le compilateur consulte la liste des surcharges de l’opérateur == et aucune d’entre elles ne prend un StdStringConverter afin de vous crier dessus.

Dans le deuxième exemple, le nom est char *. Puisqu'il s'agit d'un type primitif, le compilateur tente de convertir la non primitive en un caractère *. Puisque vous avez un remplacement en place, cela fonctionne et vous comparez les adresses.

Le compilateur n'affichera pas le transtypage de type sur des opérations n'incluant pas les types primitifs. Ce qu’il fera, c’est essayer d’utiliser des constructeurs pour faire correspondre les types. Par exemple, si vous changez votre premier exemple en ceci:

#include <string>

class StdStringConverter
{
public:
    StdStringConverter(std::string name) : m_name(name) {}
    bool operator==(const StdStringConverter &name) { return m_name == name.m_name; }
    operator const std::string () const { return m_name; }
private:
    std::string m_name;
};

int main()
{
    StdStringConverter converter(std::string("Me"));
    const std::string name = "Me";
    // Next line causes compiler error:
    // no match for 'operator==' in 'converter == name'
    return (converter == name) ? 0 : 1;
}

Le programme renvoie 0. Comme le constructeur n'est plus explicite, le compilateur tentera de l'utiliser pour convertir la chaîne en un convertisseur StdStringConverter. Comme il existe maintenant un opérateur == dans le StdStringConverter, tout fonctionne.

Les facteurs sont multiples. Si vous modifiez ainsi l'instruction return,

return (std :: operator == (nom, nom))? 0: 1;

il compile, bien qu’il ne fasse évidemment pas la même chose. D'autre part

return (std :: operator == (convertisseur, nom))? 0: 1;

ne fournit pas mais un message d'erreur plus intéressant

pas de fonction correspondante pour l'appel à & # 8216; opérateur == (StdStringConverter & amp ;, const std :: string & amp;)

qui me rappelle que l'opérateur == est basé sur un modèle sur la chaîne de base < > ;, qui doit initialiser trois paramètres de modèle. Si vous utilisez int dans votre exemple plutôt que std :: string, le compilateur ne se plaint pas.

Obtenir l’effet désiré avec std :: string est plus intriguant ...

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