2017-10-13 5 views
0

Je voudrais avoir dans ma bibliothèque un modèle de fonction func tel que l'utilisateur peut le surcharger avec ses propres types. Le problème est que mon système de type est de la formeModèle récurrence mutuelle

T := A<T>, B<T>, C

En tant que tel, template<class T> void func(A<T>); besoins template<class T> void func(B<T>); si elle est spécialisée avec T = B<C>. Réciproquement, si nous instancions func(B<T>) avec T = A<C>, la spécialisation B<T> nécessite la spécialisation A<T>.

Le problème jusqu'à ici pourrait être résolu en déclarant le modèle fonctionne dans un en-tête commun.

Ce que je ne sais pas comment aborder est de savoir comment rendre ce système de type extensible. Je voudrais que l'utilisateur puisse définir son propre type template<class T> class D<T>; et implémenter son propre template<class T> void func(D<T>);. Dans ce cas, je ne sais pas comment l'utilisateur pourrait transmettre son type de sorte que dans la spécialisation A<D<C>> la fonction void func(A<T>); est capable de trouver void func(D<T>);.

Y a-t-il un moyen standard de le faire?

Modifier minimal exemple de travail du problème:

// dans A.hpp

namespace ns { 
template<class T> struct A { T t; }; 
template<class T> 
void fun (A<T> a) { fun(a.t); } 
} 

// B.hpp

namespace ns { 
template<class T> struct B { T t; }; 
template<class T> 
void fun (B<T> b) { fun(b.t); } 

// C.hpp

#include <iostream> 
namespace other { 
template<class T> 
struct C {}; 
} 

namespace ns { 
template<class T> 
void fun(other::C<T> c) { std::cout << "C" << std::endl; } 
} 

// main.cpp

#include "A.hpp" 
#include "B.hpp" 
#include "C.hpp" 

namespace ns { 
void f() { 
    fun(A<B<other::C<int>>>()); 
} 
} 


int main() { 
    ns::f(); 
} 

Cet exemple ne compile pas. Il compile si nous réordonner les inclut dans main.cpp comme

#include "C.hpp" 
#include "B.hpp" 
#include "A.hpp" 

Maintenant, cela est clairement un hack. Avec cette conception, l'utilisateur ne pourra pas instancier à la fois A<B<C<int>>> et B<A<C<int>>>. Une solution à cela serait de transmettre les déclarations A et B dans un autre modèle et de l'inclure à la fois dans A.hpp et B.hpp. Le problème vient maintenant lorsque vous essayez de laisser l'utilisateur de la bibliothèque définir ses propres types. Si l'utilisateur de la bibliothèque définit son propre type template<class T> class D;, elle ne peut pas transmettre la déclaration, et si elle tente d'instancier A<D<C<int>>>, la compilation échouera.

Dans cet exemple, l'espace de noms other représente un espace de noms sur lequel je n'ai aucun contrôle et C représente une classe préexistante dans une autre bibliothèque. Cela peut être considéré comme une classe boost ou similaire. L'espace de noms ns est celui que ma bibliothèque définit.

+3

S'il vous plaît montrer un code réel. –

+0

Je pourrais deviner ce que vous entendez par "de la forme", mais je pourrais facilement me tromper. Le code réel qui dessine l'idée élimine une grande partie de cette ambiguïté. 'T: = A ' est à tout le moins abuser d'un T réutilisant pour signifier deux choses différentes sur la même déclaration, et je suis incertain si cette virgule indique une étrange récusion ou ce n'est qu'une liste de ce que 'T' peut être. Code réel en éditant la question démontrant votre plan s'il vous plaît (ce code devrait compiler) – Yakk

+0

Par ': =' Je voulais définir une grammaire sur le terme 'T'. J'essaye de reproduire ce comportement dans un court exemple, où la compilation de code dépend de l'ordre des inclusions. – Lezkus

Répondre

1

Que se passe-t-il si fun() est une méthode statique dans une classe modèle?

Donc vous pouvez spécialiser partiellement la classe?

Je veux dire quelque chose comme

// 000.h

#ifndef h_000__h 
#define h_000__h 

namespace ns 
{ 
    template <typename T> 
    struct foo; 
} 

#endif 

// 001.h

#ifndef h_001__h 
#define h_001__h 

#include <iostream> 

#include "000.h" 

namespace ns 
{ 
    template<class T> 
    struct A 
    { T t; }; 

    template <typename T> 
    struct foo<A<T>> 
    { 
     static void fun (A<T> a) 
     { std::cout << "A<T> fun" << std::endl; foo<T>::fun(a.t); } 
    }; 
} 

#endif 

// 002.h

#ifndef h_002__h 
#define h_002__h 

#include <iostream> 

#include "000.h" 

namespace ns 
{ 
    template <typename T> 
    struct B 
    { T t; }; 

    template <typename T> 
    struct foo<B<T>> 
    { 
     static void fun (B<T> a) 
     { std::cout << "B<T> fun" << std::endl; foo<T>::fun(a.t); } 
    }; 
} 

#endif 

// 003.h

#ifndef h_003__h 
#define h_003__h 

#include <iostream> 

#include "000.h" 

namespace other 
{ 
    template <typename T> 
    struct C 
    { }; 
} 

namespace ns 
{ 
    template <typename T> 
    struct foo<other::C<T>> 
    { 
     static void fun (other::C<T> a) 
     { std::cout << "C<T> fun" << std::endl; } 
    }; 
} 

#endif 

// main.cpp

#include "001.h" 
#include "002.h" 
#include "003.h" 

namespace ns 
{ 
    void f() 
    { 
     using type = A<B<other::C<int>>>; 

     foo<type>::fun(type{}); 
    } 
} 

int main() 
{ 
    ns::f(); // print A<T> fun \n B<T> fun \n C<T> fun \n 
} 
+0

C'est exactement la solution que je recherchais. Je vous remercie! – Lezkus