2010-03-28 7 views
4

J'ai un tas de fonctions surchargées qui fonctionnent sur certains types de données tels que int, double et chaînes. La plupart de ces fonctions effectuent la même action, où seul un ensemble spécifique de types de données est autorisé. Cela signifie que je ne peux pas créer une simple fonction de modèle générique car je perds la sécurité de type (et potentiellement un problème d'exécution pour la validation au sein de la fonction).Fonction semi-générique

Est-il possible de créer une "fonction de sécurité de type temps de compilation semi-générique"? Si c'est le cas, comment? Sinon, est-ce quelque chose qui apparaîtra en C++ 0x?

Une idée (non valide);

template <typename T, restrict: int, std::string > 
void foo(T bar); 
... 
foo((int)0); // OK 
foo((std::string)"foobar"); // OK 
foo((double)0.0); // Compile Error 

Note: Je me rends compte que je pouvais créer une classe qui a surchargé les constructeurs et les opérateurs d'affectation et de transmettre une variable de cette classe plutôt à la fonction.

+1

Je pense que l'hypothèse que vous n'avez la sécurité de type sans restriction est quelque peu suspecte. En outre, les types ne sont définitivement pas vérifiés lors de l'exécution. – UncleBens

+0

UncleBens: Il n'y a "aucune sécurité de type" dans la mesure où l'exemple ci-dessus pour les doubles serait compiler. –

Répondre

5

Utilisez sfinae

template<typename> struct restrict { }; 
template<> struct restrict<string> { typedef void type; }; 
template<> struct restrict<int> { typedef void type; }; 

template <typename T> 
typename restrict<T>::type foo(T bar); 

Ce foo ne sera en mesure d'accepter ou stringint pour T. Aucune erreur de compilation dure ne se produit si vous appelez foo(0.f), mais plutôt s'il y a une autre fonction qui accepte l'argument, celui-ci est pris à la place.

+0

Le code posté ne compile même pas dans VC++ 9. ("trop ​​peu d'arguments de gabarit" sur la deuxième ligne [parmi d'autres erreurs]) –

+0

Je ne peux pas éditer votre code, mais ajouter le gabarit <> avant que "struct restrict ..." ne rende ce travail. Template vodoo .. –

+0

Oops, je suis désolé j'étais endormi quand j'ai écrit cela. –

1

Vous pouvez créer une fonction gabarit "privée" qui n'est jamais exposée à l'extérieur et l'appeler à partir de vos surcharges "sécurisées". D'ailleurs, il y a généralement le problème d'exposer directement la version modélisée: si le type passé n'est pas correct, une erreur de compilation sera émise (sauf si vous savez que votre algorithme peut exposer des bugs subtils avec certains types de données).).

+0

Vous devez prendre en compte les conversions implicites. Attention si vous avez une surcharge 'foo (double)' mais que vous voulez le désactiver pour tous les types entiers, etc – UncleBens

+0

Pas vraiment utile car j'ai encore besoin de garder ces fonctions publiques présentes .. –

+0

@UncleBens: oui, vous avez [email protected] Ullner: quel est le problème? Créez simplement la version du modèle uniquement dans le fichier .cpp (en l'appelant d'une manière légèrement différente), et ne laissez que les surcharges dans le fichier .hpp (ou .h) à utiliser à partir d'autres unités de compilation. –

0

Peut-être un peu solution laid, mais foncteurs pourrait être une option:

class foo { 
    void operator()(double); // disable double type 
public: 
    template<typename T> 
    void operator()(T bar) { 
     // do something 
    } 
}; 

void test() { 
    foo()(3); // compiles 
    foo()(2.3); // error 
} 

Edit: Je Inversed ma solution

class foo { 
    template<typename T> 
    void operator()(T bar, void* dummy) { 
    // do something 
    } 
public: 
    // `int` is allowed 
    void operator()(int i) { 
     operator()(i, 0); 
    } 
}; 

foo()(2.3); // unfortunately, compiles 
foo()(3); // compiles 
foo()("hi"); // error 
+0

Idée intéressante, mais cela signifierait qu'il faudrait penser à tous les types de données potentiels qui ne sont pas valides ... De qui il existe un nombre infini (pratique) .. –

+0

@Fredrik, oui c'est un problème. J'ai mis à jour ma réponse. –

0

Vous pourriez probablement travailler avec des spécialisations des modèles pour le « restreint "types que vous voulez autoriser. Pour tous les autres types, vous ne fournissez pas de spécialisation de modèle, de sorte que le modèle générique "basic" sera utilisé. Là, vous pouvez utiliser quelque chose comme BOOST_STATIC_ASSERT pour lancer une erreur de compilation.

Voici quelques pseudo-code pour clarifier mon idée:

template <typename T> 
void foo(T bar) {BOOST_STATIC_ASSERT(FALSE);} 

template<> // specialized for double 
void foo(double bar) {do_something_useful(bar);}; 
+0

La syntaxe correcte est une surcharge hors modèle. :) – UncleBens

+0

Je ne trouve pas la documentation appropriée pour le "typename: spécialisé pour double" ... Quelqu'un? –

+0

'template <> void foo (double barre);' Mais il est recommandé de ne pas spécialiser les fonctions du template: 'void foo (double barre);' – UncleBens

0

Pour lister une sélection arbitraire de types je suppose que vous pourriez utiliser une typelist. E.g voir le last part of my earlier answer.

L'usage peut-être quelque chose comme:

//TODO: enhance typelist declarations to hide the recursiveness 
typedef t_list<std::string, t_list<int> > good_for_foo; 

template <class T> 
typename boost::enable_if<in_type_list<T, good_for_foo> >::type foo(T t);