2010-02-19 8 views
4

Comment l'opérateur bool() peut-il provoquer une erreur lors de la déclaration de l'opérateur std :: string dans une classe et servant également de conversion implicite en chaîne par lui-même?opérateur bool() converti en std :: string et en conflit avec l'opérateur std :: string()

#include <iostream> 
#include <string> 
using namespace std; 

class Test { 
public: 
    operator std::string() { cout << "op string" << endl; return "whatever";} 
    operator bool() { cout << "op bool" << endl; return true;} 
}; 

int main(int argc, char *argv[]) { 
    string s; 
    Test t; 
    s = t; 
} 
+2

Si vous allez de l'avant et que vous utilisez 'namespace std;' pourquoi allez-vous préciser 'std :: string'? –

Répondre

8

Le problème que vous rencontrez (en plus de operator std::string() retournant un booléen) est que les conversions implicites se déclenchent quand vous voulez et quand vous ne le faites pas.

Lorsque le compilateur voit s = t il identifie les std::operator= potentiels matchs suivants:

// using std::string for compactness instead of the full template 
std::string::operator=(std::string const &); 
std::string::operator=(char); 

Maintenant, t est ni d'eux, donc il essaie de convertir à quelque chose qui peut s'adapter et trouve deux chemins: convertir en bool qui peut être promu à char ou convertir en std::string directement. Le compilateur ne peut pas vraiment décider et abandonne.

C'est l'une des raisons pour lesquelles vous voulez éviter de fournir plusieurs opérateurs de conversion différents. Tout ce qui peut être implicitement appelé par le compilateur sera éventuellement appelé quand vous ne le pensez pas.

Cette article traite spécifiquement de ce problème. La suggestion est au lieu de fournir une conversion à bool, fournir une conversion à une fonction de membre

class testable { 
    typedef void (testable::*bool_type)(); 
    void auxiliar_function_for_true_value() {} 
public: 
    operator bool_type() const { 
     return condition() ? &testable::auxiliar_function_for_true_value : 0; 
    } 
    bool condition() const; 
}; 

Si une instance de cette classe est utilisée dans une condition (if (testable())) le compilateur va essayer de se convertir à l'bool_type qui peut être utilisé dans un état.

EDIT:

Après le commentaire sur la façon dont le code est plus complexe avec cette solution, vous pouvez toujours fournir un petit utilitaire générique. Une fois que vous avez fourni la première partie du code, la complexité est encapsulée dans l'en-tête.

// utility header safe_bool.hpp 
class safe_bool_t; 
typedef void (safe_bool_t::*bool_type)(); 
inline bool_type safe_bool(bool); 

class safe_bool_t { 
    void auxiliar_function_for_true_value() {} 
    friend bool_type safe_bool(bool); 
}; 
inline bool_type safe_bool(bool) 
{ 
    return condition ? &safe_bool_t::auxiliar_function_for_true_value : 0; 
} 

Votre classe devient beaucoup plus simple, et il est lisible en lui-même (en choisissant des noms appropriés pour les fonctions et types):

// each class with conversion 
class testable { 
public: 
    operator bool_type() { 
     return safe_bool(true); 
    } 
}; 

Seulement si le lecteur est intéressé à savoir comment la idiome safe_bool est mis en œuvre et lit l'en-tête, ils remplissent être confrontés à la complexité (qui peut être expliqué dans les commentaires)

+0

+1 pour avoir mentionné l'idiome 'safe_bool'. La plupart du temps, vous ne voulez pas vraiment un booléen, vous voulez juste pouvoir écrire le test 'if' :) –

+0

Je suppose que bool est converti en char par la promotion d'entier, puisque le standard ne dit rien sur conversions de bool en char. Citant: "Une valeur de type bool peut être convertie en une valeur de type int, avec false étant nul et vrai devenant un." – piotr

+0

article très intéressant, mais je trouve qu'il est très illisible d'utiliser un tel code pour fournir un test si, il va à l'encontre de l'objectif d'ajouter de la lisibilité. Aussi la fonction auxiliaire devrait être const. – piotr

4

Votre opérateur std :: string() a besoin de renvoyer une chaîne, pas un booléen.

+0

Il renvoie une chaîne, 'std :: string (static_cast (true))' – MSalters

+0

Correction de la faute de frappe, mais cela n'a rien à voir avec la question – piotr

1

Comme le souligne David Rodriguez correctement sur un bool peut être promu à un char vous obtenez une surcharge ambigüe.

Dans la base de données, la mise à l'essai d'une classe est généralement effectuée via une conversion en void *, par ex. quand vous faites

while (istream.getline()) { 
} 

La condition de boucle résout false car istream renvoie NULL dans son operator void*.

Certaines personnes soutiennent que ce n'est pas bon d'une solution comme en théorie on pourrait faire

void* streamptr = istream; 
delete streamptr; 

Mais à mon avis, si quelqu'un commence à supprimer des pointeurs comme ça ... il ne devrait pas être autorisé n'importe où près du code stl (ou C++ d'ailleurs).

Questions connexes