[C++] partial specialisation van enkele functie

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Darkvater
  • Registratie: Januari 2001
  • Laatst online: 26-08-2024

Darkvater

oh really?

Topicstarter
Ik heb de volgende klasse (klein voorbeeldje, maar komt architectisch wel overheen met mijn probleem)
C++:
1
2
3
4
5
6
7
8
typedef enum direction_t {DOWN = 0, UP = 1};

template <class T, direction_t U> class my_class {
public:
  int test();
  /* heleboel andere functies, etc. */
};
template <class T, direction_t U> int  my_class<T, U>::test{cout << "direction is " << U << endl;}


Nu wil ik de functie test() specialiseren voor DOWN en UP. Dus eigenlijk wil ik zoiets doen:
C++:
1
2
3
4
5
6
7
8
9
typedef enum direction_t {DOWN = 0, UP = 1};

template <class T, direction_t U> class my_class {
public:
  int test();
  /* heleboel andere functies, etc. */
};
template <class T> int my_class<T, DOWN>::test() {cout << "direction is down" << endl;}
template <class T> int my_class<T,   UP>::test() {cout << "direction is up" << endl;}


MSVC geeft echter de foutmelding
error C3860: template argument list following class template name must list parameters in the order u sed in template parameter list
error C2976: 'my_class<T, direction_t>': too few template arguments
Dit kan kennelijk dus niet. Ik wil echter niet de hele klasse specialisen, eg template <class T> class my_class<T, DOWN> {...}; want dan moet ik of de hele klasse opnieuw implementeren en alle members dupliceren (template specialisation kan niet bij "general" klasse, is totaal op zich), of class inheritance doen. Dat wilde ik ook niet. Ik wil juist deze functie in tweeen splitsen zodat er niet in de functie test() een if (direction == DOWN) conditie staat. Is er een manier om het wel te doen op de manier die ik wil? :)


Windows Vista? *NEVER* Het waarom - Opera forever!!!
I've seen chickens that were more menacing. Chickens in a coma. On ice. In my fridge


Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Delegeren: laat jouw test() altijd template <typename T> void my_class_test_impl(); aanroepen, en specialiseer die functie.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Acties:
  • 0 Henk 'm!

  • Darkvater
  • Registratie: Januari 2001
  • Laatst online: 26-08-2024

Darkvater

oh really?

Topicstarter
Maar die functie moet wel een member van de klasse blijven, anders kan ik nog niet bij alle data. Ik kan dus niet gewoon een "globale" template functie maken want dan moeten alle parameters, of een pointer naar de klasse doorgegeven worden.


Windows Vista? *NEVER* Het waarom - Opera forever!!!
I've seen chickens that were more menacing. Chickens in a coma. On ice. In my fridge


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-09 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

Wat is er mis met het doorgeven van een pointer (of reference) naar die class? Kun je daar ook meteen op specializen.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
template<class T> void my_class_test_impl(my_class<T, DOWN> &)
{
    cout << "direction is down" << endl;
}

template<class T> void my_class_test_impl(my_class<T, UP> &)
{
    cout << "direction is up" << endl;
}

template<class T, direction_t U> int my_class<T,U>::test()
{
    my_class_test_impl(*this); // de compiler doet de rest
}

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Of het gewoon run-time doen. In je ctor geef je een enum UP/DOWN mee, en op basis daarvan zet je een functie pointer naar de correcte test() implementatie. Dan roep je ipv test() m_test_ptr() aan. Kan je ook nog run-time kiezen welke je wilt hebben. Enige overhead is een indirecte functie call ipv een directe (en extra storage), maar als dat vaak gebeurt staat het toch in cache, en als het niet vaak gebeurt... dan maakt het toch niet uit.

(Voor een micro class waarvan je er miljoenen aanmaakt, zoals een 2D punt oid, zou ik dit niet doen overigens.)

[ Voor 13% gewijzigd door Zoijar op 25-08-2009 12:32 ]


Acties:
  • 0 Henk 'm!

  • cpt.spiff
  • Registratie: Augustus 2009
  • Laatst online: 20-02 14:29
Je topic titel geeft al aan dat je een partial specialization van een functie (-template) wilt. Dat bestaat echter niet. Zie bijvoorbeeld dit artikel van Herb Sutter, waar ook letterlijk de tekst staat "there’s no such thing as a partial specialization of a function template". Dat het hier om een member functie gaat doet daar niet aan af, behalve dan dat de compiler in de war raakt (en gcc er bijvoorbeeld van uitgaat dat je toch een member function van een partial class specialization aan het definieren bent).

Het alternatief is overloading. .oisyn gaf daar al een voorbeeld van. Als je echt member functies wilt hebben—bijvoorbeeld omdat je toegang wilt hebben tot de interne state van je class—dan kun je bijvoorbeeld een wrapper memberfunctie maken die vervolgens tag-dispatching gebruikt om de 'specialisatie' (eigenlijk: overload) van je functie aan te roepen. Bijvoorbeeld:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum direction_t {DOWN = 0, UP = 1};

template <class T, direction_t U> class my_class {
public:
  int test() 
  {
    return do_test( tag<U>());
  }
  /* heleboel andere functies, etc. */
private:
  template <direction_t> struct tag {};

  int do_test( const tag<DOWN> &)
  {
    cout << "direction is down\n";
    return 1;
  }

  int do_test( const tag<UP> &)
  {
    cout << "direction is up\n";
    return 2;
  }
};


Je hebt hier een speciaal tag-template nodig omdat direction_t een non-type template argument is. Wanneer je had willen specialiseren op T had je bijvoorbeeld ook kunnen overloaden op T * (alhoewel een tag-type wat explicieter is).

Overigens heeft de free-function aanpak van .oisyn het voordeel dat de specialisatie 'open' is, je kunt overloads toevoegen zonder je class interface aan te passen, dus ik zou het bovenstaande alleen gebruiken wanneer je per se het gedrag wilt sturen via een enum value (in plaats van bijvoorbeeld een policy class) en de implementatie per se een member-functie moet zijn.

[ Voor 11% gewijzigd door cpt.spiff op 26-08-2009 00:58 . Reden: code oppoetsen, toevoegen van mitsen en maren aan het eind. ]

Pagina: 1