2009-10-26 8 views
4

Je veux fusionner en quelque sorte des modèles comme ceux-ci en un seul:Comment puis-je transmettre un opérateur arithmétique à un modèle?

template <class Result, class T1, class T2> 
class StupidAdd 
{ 
public: 
    T1 _a; T2 _b; 
    StupidAdd(T1 a, T2 b):_a(a),_b(b) {} 
    Result operator()() { return _a+_b; } 
}; 

template <class Result, class T1, class T2> 
class StupidSub 
{ 
public: 
    T1 _a; T2 _b; 
    StupidSub(T1 a, T2 b):_a(a),_b(b) {} 
    Result operator()() { return _a-_b; } 
}; 

(suivi du même code pour Mul, Div, etc.) où tout le code est le même, sauf pour le réel « + », "-" (et "StupidAdd", "StupidSub", etc).

Ces "foncteurs" stupides sont ensuite utilisés par un autre modèle. Comment puis-je éviter la répétition, SANS le préprocesseur? (La raison pour laquelle je suis entré dans des modèles était d'éviter le préprocesseur)

Autrement dit, comment puis-je transmettre des opérateurs arithmétiques dans un modèle?

+0

Essayez-vous de surcharger ces opérateurs ou est-ce juste un exemple? – Jacob

+0

Je ne surcharge pas les opérateurs arithmétiques - Je veux juste pouvoir les passer dans un template, quelque chose comme template Résultat DoWork (A a, B b) {return ArithOp (a, b); } ... sans définir des choses stupides comme StupidAdd, StupidSub – OldCoder

Répondre

7

Peut-être que vous pourriez utiliser std::plus<T>, std::minus<T>, std::multiplies<T> et std::divides<T>. Cependant, ceux-ci ne fonctionneront que si les deux opérandes sont du même type, ou probablement si le premier peut être converti en le premier.

Je ne vois aucun moyen de réaliser ce que vous essayez de faire, sauf en utilisant le préprocesseur. De bonnes raisons de ne pas vouloir de macros?

Si vous voulez vous assurer que le type de retour est assez grand pour contient le résultat, vous pouvez faire quelque chose le long de cette façon:

#include <functional> 
#include <boost/mpl/if_.hpp> 

// Metafunction returning the largest type between T and U 
// Might already exist in Boost but I can't find it right now...maybe 
// boost::math::tools::promote_args 
template <typename T, typename U> 
struct largest : 
    boost::mpl::if_< 
     boost::mpl::bool_<(sizeof(T) > sizeof(U))>, 
     T, 
     U 
    > 
{}; 

template <typename T, typename U, template <typename S> class Op> 
struct Foo 
{ 
    typedef typename largest<T, U>::type largeType; 

    largeType bar(const T & t, const U & u) 
    { 
     return Op<largeType>()(t, u); // Applies operator+ 
    } 
}; 

int main() 
{ 
    Foo<int, double, std::plus> f; 
    double d = f.bar(12, 13.0); // takes int and double, returns double 
} 

Ici, j'ai utilisé Boost MPL pour écrire le largest métafonction, mais vous pouvez écrivez votre propre métafonction if si vous ne pouvez pas utiliser Boost (template de classe paramétré par deux types et un bool, spécialisé pour vrai et faux).

Pour déterminer le type de retour d'une expression, vous pouvez également regarder boost::result_of qui, si je comprends bien, est équivalent à l'opérateur decltype à venir en C++ 0x.

+0

Je ne peux pas - je suis dépendant du fait que l'opérateur + (int, double) renvoie un double. std :: plus a besoin que les deux arguments soient du même type. – OldCoder

+0

La raison principale pour laquelle je déteste "cpp" est qu'elle fait du débogage un véritable enfer - le moment où vous commencez à utiliser des macros est le moment où vous perdez la possibilité de faire un pas à pas dans gdb ... – OldCoder

+0

refléter ce fait :). Eh bien, je ne vois aucun moyen de le faire. Sauf s'il est possible de prendre l'adresse d'un opérateur intégré, ce dont je doute. –

3

Merci Luc, c'est très cool. Je finalement fait d'une manière plus simple:

#include <functional> 

template < 
    class Result, 
    class T1, 
    class T2, 
    template <class ReturnType> class BinaryOp> 
class Stupido 
{ 
public: 
    T1 _a; T2 _b; 
    Stupido(T1 a, T2 b):_a(a),_b(b) {} 
    Result operator()() { return BinaryOp<Result>()((Result)_a,(Result)_b); } 
}; 

Et utilisé le « plus », « moins » lors de l'instanciation Stupido. La conversion en "Résultat" était suffisante pour mes besoins (int + double => double + double => double)

0

Je voudrais utiliser le CTD 0x decltype et la nouvelle définition de auto. Vous aurez toujours besoin de définir les classes, mais en les utilisant, vous pouvez faire un travail plus agréable de les définir. Ils seront encore à peu près autant de travail à définir, mais au moins en utilisant ils seront considérablement plus propres. En particulier, vous pouvez utiliser decltype/auto pour déduire le type de retour correct au lieu de devoir le spécifier explicitement.

Ils sont disponibles avec un bon nombre de compilateurs récents - Intel C++, g ++, Comeau, la version bêta de VC++ 2010, et même la dernière itération de Borland/Inprise/Embarcadero/nouveau nom de cette semaine.

3

Je pense qu'il ya une amélioration à la solution de OldCoder:

#include <functional> 

template <class Result, 
      template <class Result> class BinaryOp> 
struct Stupido 
{ 
    template <typename T1, typename T2> 
    Result operator()(const T1& a, const T2& b) { return BinaryOp<Result>()((Result)a,(Result)b); } 
}; 

De cette façon, l'appel peut être fait que:

Stupido<int, std::plus > stup; 
int result = stup(3.0f, 2.0); 

et le même objet de fonction peut être utilisé avec plusieurs opérandes, il pourrait être passé à un appel std::transform.

Je crois qu'il doit y avoir un moyen de supprimer un résultat de la déclaration de modèle, mais je suis incapable de le trouver.

+3

template class BinaryOp> struct Stupido {...}; –