2010-10-07 4 views
0

J'ai une méthode et deux classes définies comme suit:Réduction du nombre d'arguments de modèle pour la classe

template<template<class X> class T> 
void doSomething() 
{ 
    T<int> x; 
} 
template <class T> 
class ClassWithOneArg 
{ 
    T t; 
}; 

template <class T1, class T2> 
class ClassWithTwoArgs 
{ 
    T1 t1; 
    T2 t2; 
}; 

Je peux maintenant

doSomething<ClassWithOneArg>(); 

mais je ne peux pas

doSomething<ClassWithTwoArgs>(); 

Cependant, J'aimerais passer ClassWithTwoArgs à doSomething, où T2 = double.

La seule méthode que je trouve est de créer

template <class T1> 
class ClassWithTwoArgs_Child 
    : public ClassWithTwoArgs<T1, double> 
{ 
}; 

puis

doSomething<ClassWithTwoArgs_Child>(); 

Cela fonctionne, mais dans mon cas concret toutes les classes exigent un argument du constructeur et donc je dois créer un constructeur avec cet argument également dans la classe _Child et le passer à la base que je veux vraiment éviter.

Avez-vous une idée de comment faire cela?

Merci beaucoup!

Répondre

3

Indirection est une solution. Au lieu d'un paramètre de modèle de modèle que vous passez une « fonction méta » - une fonction qui d'un type à l'autre sous la forme d'une structure avec un modèle de classe imbriquée:

struct mf1 { 
    template<class Arg1> 
    struct eval { 
    typedef ClassTemplateWithOneArg<Arg1> type; 
    }; 
}; 

template<class Arg2> 
struct mf2 { 
    template<class Arg1> 
    struct eval { 
    typedef ClassTemplateWithTwoArgs<Arg1,Arg2> type; 
    }; 
}; 

template<class MetaFunc> 
void do_something() 
{ 
    typedef typename MetaFunc::template eval<int>::type clazztype; 
    clazztype x; 
} 

void foo() { 
    do_something<mf1>(); 
    do_something<mf2<double> >(); 
} 

En C++ 0x cela pourrait être réduit à un « modèle typedef »:

template<class Arg1> 
using NewClassTemplate = ClassTemplateWithTwoArgs<Arg1,double>; 

qui vous permet de passer NewClassTemplate comme argument de modèle de modèle qui accepte également un seul paramètre de modèle.

+0

Notez le commentaire de Philip (j'ai peur que ce n'est pas ce que je cherche, car il pourrait y avoir d'autres cas où j'aimerais aussi passer "ClassWithTwoArgs ".). Cette solution ne fonctionnera pas car le 'double' est codé en dur – Chubsdad

+0

@chubsdad, bien sûr, vous pouvez paramétrer la fonction méta. Découvrez comment j'ai transformé mf2 en un modèle lui-même et comment il est utilisé. – sellibitze

+0

merci, cela a résolu ma question! Ce n'est pas un problème de changer "doSomething" mais je ne voulais pas avoir à définir le Template-Argument ("int" dans mon exemple) de l'extérieur de "doSomething". – Philipp

0

Cela fonctionne avec MSVC:

template<class T> 
void doSomething() 
{ 
    T x; 
} 

// class definitions omitted... 

void test() { 
    doSomething<ClassWithOneArg<int> >(); 
    doSomething<ClassWIthTwoArgs<int, double> >(); 
} 

Je ne comprends pas très bien pourquoi vous souhaitez définir le premier paramètre de votre paramètre de modèle de modèle pour être int à l'intérieur de doSomething. On dirait une "odeur de gabarit" pour moi, puisque doSomething doit en savoir beaucoup sur son paramètre de modèle de modèle.

Ne serait-il pas plus propre d'appeler doSomething comme je l'ai proposé? (Mais évidemment je ne connais pas le contexte de vos appels).

+0

Je crains que ce n'est pas ce que je cherche, parce qu'il pourrait y avoir d'autres cas où je voudrais aussi passer « ClassWithTwoArgs ». – Philipp

+0

Vous voulez que dans 'doSomething' le premier paramètre soit toujours' int' mais le second paramètre soit défini lors de l'appel 'doSomething'? – WolfgangA

+0

Notez que j'ai édité ma réponse – WolfgangA

1

Il n'y a pas de solution générique. Votre meilleur pari est

template<class T> 
void doSomething() 
{ 
    T x; 
} 
template <class T> 
class ClassWithOneArg 
{ 
    T t; 
}; 

template <class T1, class T2 = double> 
class ClassWithTwoArgs 
{ 
    T1 t1; 
    T2 t2; 
}; 

int main(){ 
    doSomething<ClassWithOneArg<int>>(); 
    doSomething<ClassWithTwoArgs<int, double> >(); 
} 
1

Il semble que ce que vous recherchez est similaire à la reconsolidation de allocateurs (donné un allocateur, les conteneurs doivent être en mesure de produire un allocateur pour un type différent - par exemple std::list<int> pourrait avoir besoin d'une allocator<list_node<int> > de allocator<int>

. Cependant, les modèles de classe devraient être modifiés pour cela.

template<class T> 
void doSomething(const T&) 
{ 
    typename T::template rebind_1st<int>::type x; 
} 

template <class T> 
class ClassWithOneArg 
{ 
    T t; 
public: 
    template <class U> 
    struct rebind_1st { typedef ClassWithOneArg<U> type; }; 
}; 

template <class T1, class T2> 
class ClassWithTwoArgs 
{ 
    T1 t1; 
    T2 t2; 
public: 
    template <class U> 
    struct rebind_1st { typedef ClassWithTwoArgs<U, T2> type; }; 
}; 

int main() 
{ 
    doSomething(ClassWithOneArg<char>()); 
    doSomething(ClassWithTwoArgs<char, double>()); 
} 
1

En supposant que vous voulez déclarer instanciation de modèle de la même classe avec un type différent pour le premier paramètre de modèle, il apparaît une version du code de visiteur est possible qui ne nécessite pas de modifier les classes d'origine.

template <class T, class NewFirstArg> 
struct rebind_1st; 

template <template <class> class T, class Arg1, class NewFirstArg> 
struct rebind_1st<T<Arg1>, NewFirstArg> 
{ 
    typedef T<NewFirstArg> type; 
}; 

template <template <class, class> class T, class Arg1, class Arg2, class NewFirstArg> 
struct rebind_1st<T<Arg1, Arg2>, NewFirstArg> 
{ 
    typedef T<NewFirstArg, Arg2> type; 
}; 

template <class T> 
void foo() 
{ 
    typename rebind_1st<T, int>::type x; 
    (void)x; 
} 

template <class T> 
struct One{}; 

template <class T1, class T2> 
struct Two{}; 

int main() 
{ 
    foo<One<char> >(); 
    foo<Two<char, double> >(); 
} 
Questions connexes