2010-12-07 3 views
2

J'ai une classe modélisée contenant une valeur. Est-il possible d'ajouter un constructeur à la classe pour permettre la conversion implicite comme dans l'exemple ci-dessous?ajouter un constructeur à la classe de modèles

Ou y a-t-il une meilleure façon de faire cela?

#include <string> 

template<typename T> 
class Value 
{ 
    public: 
    Value(const T& value) : m_value(value) { }; 
    private: 
    T m_value; 
}; 

// I thought adding something like this would do the trick but it does not work: 
/* 
template<> 
class Value<std::string> 
{ 
    public: 
    Value(const char *sz) : m_value(sz) { }; 
} 
*/ 

void f(const Value<std::string> &s) 
{ 
} 

int main() 
{ 
    f(std::string("hello")); 

    // I want to have this working: 
    f("hello"); 
} 

Répondre

4

Appel f(const Value<std::string>&) avec une chaîne littérale nécessiterait deux conversions définies par l'utilisateur (const char[] ==>std::string ==>Value<std::string>) afin de faire correspondre les paramètres de la fonction, alors que la norme ne permet un.
Je vois deux possibilités pour résoudre cela: soit surcharger le constructeur, soit surcharger f().

En supposant que vous posez des questions sur le premier parce que ce dernier n'est pas possible, il existe plusieurs façons de surcharger le constructeur.

Vous pouvez exploiter le fait que les fonctions membres d'un modèle de classe ne sont compilées que si elles sont appelées, et ajoutez un constructeur qui ne compile que lorsque T est d'un certain type. Bien sûr, si les utilisateurs de votre modèle l'invoquent pour d'autres types, cela entraînera une erreur. Cependant, au lieu de voir un problème dans ce que vous pouvez embrasser en faisant le constructeur d'un modèle de membre:

template<typename U> 
Value(const U& value) : m_value(value) { }; 

De cette façon, tout ce qui peut être converti en T (ainsi que T lui-même, bien sûr) est autorisé pour U.

Ou vous pourriez spécialiser la classe pour std::string. Malheureusement, vous devrez alors spécialiser toute la classe car il n'y a pas de spécialisation sélective des membres individuels. Dans ce cas, vous pouvez déplacer tout le code dans une classe de base private (Value), avec le modèle de base Value définissant simplement les constructeurs qui redirigent vers les constructeurs de la classe de base, et une spécialisation Value<std::string> qui ajoute un autre constructeur prenant const char* .

+0

Je pensais aussi qu'il était implicitement convertible mais le compilateur (g ++) s'en plaint (initialisation invalide de référence). – rve

+0

et non, la chaîne n'est pas un espace réservé – rve

+0

@rve: Je suis désolé, c'était un brainfart à moi. J'ai échoué à voir que vous vouliez appeler 'f()' avec une chaîne C. Je vais réparer ma réponse. – sbi

1

Vous ne pouvez pas ajouter à la classe comme ça, mais vous pouvez spécialiser la classe entière.

1

Essayez this:

template<typename T> 
class Value 
{ 
    public: 
    Value(const T& value) : m_value(value) { } 
    Value(const char* a): m_value(a){} // add this 
    private: 
    T m_value; 
}; 
+0

Bien sûr cela fonctionne mais cela n'a pas beaucoup de sens pour la valeur rve

+0

@rve: Ma seule intention était de rendre votre code exempt d'erreurs de compilation ;-) –

2

Vous ne pouvez pas. Ceci est une décision explicite par les concepteurs C++. la raison en est que le compilateur aurait besoin d'aller à la chasse pour les conversions possibles, et en général ce serait une chasse infinie.

Bien sûr , vous pensez que const char[] ==>std::string ==>Value<std::string> est logique, mais le compilateur n'a pas std::string. Il a juste const char[] ==> ??? ==>Value<std::string>, et il faudrait trouver un type au milieu. Par exemple, il peut y avoir un class Foo quelque part qui a un constructeur Foo::Foo(const char*), et un Foo::operator Value<std::string>() const. Cela fonctionnerait aussi.

Comme vous pouvez le voir, il n'y a rien dans const char[] ou Value<std::string> qui pointe vers Foo. Par conséquent, le compilateur devrait partir à la chasse à l'aveugle.

En tant qu'écrivain de Value, vous avez cependant le choix.Vous pouvez informer le compilateur qu'un Value<std::string> peut être construit à partir de tout type que std :: string :: string acceptera:

template<typename T> 
class Value 
{ 
    public: 
    Value(const T& value) : m_value(value) { }; 
    // Intentionally not "explicit" 
    template<typename U> Value(const U& value) : m_value(value) { }; 
    private: 
    T m_value; 
}; 

Maintenant, si vous appelez void f(const Value<std::string> &s) comme f("hello"), il y a une seule conversion implicite Value<std::string>::Value<const char*>(const char* const&)

Questions connexes