[C++] Hoe operator<< definiëren voor enum in class template?

Pagina: 1
Acties:

  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 118% gewijzigd door Eelis op 18-02-2015 19:36 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 04:03
Ik heb wel een workaround voor je; expliciet de juiste operator<< aanroepen:
C++:
1
operator<< <V> (std::cout, e);

Maar waarschijnlijk is dat niet je bedoeling (als je zulke ingewikkelde klassen aan het verzinnen bent, had je dat waarschijnlijk allang bedacht).

De reden van de problemen lijkt me dat een enum-declaratie niet echt een zelfstandig type is, maar alleen wat constanten produceert. Dat is mijn speculatie; geen idee hoe dat in de standaarddocumentatie zit.

Overigens heb ik het ook onder Visual C++ 7.1 geprobeerd en daar treed hetzelfde probleem op. De work-around werkt daar trouwens niet; waarom precies is me onduidelijk. Ik krijg dit soort meldingen:
<knip>\Vc7\include\ios(24) : error C2039: 'int_type' : is not a member of 'V'
<knip>\test.cpp(12) : see declaration of 'V'
<knip>\include\ostream(36) : see reference to class template instantiation 'std::basic_ios<_Elem,_Traits>' being compiled
with
[
_Elem=char,
_Traits=V
]
<knip>\test.cpp(17) : see reference to class template instantiation 'std::basic_ostream<_Elem,_Traits>' being compiled
with
[
_Elem=char,
_Traits=V
]
Ik heb het idee dat Visual C++ 7.1 op de een of andere manier de namespaces opfukt, want ik zie niet in waarom 'ie bij de declaraties uit de iostream-header zou moeten komen, als operator<< uit de default namespace aangeroepen wordt.

edit:
Ik heb trouwens nog wel een semi-gerelateerd vraagje, dat ik eventueel wel stellen als het geen probleem is dat ik je topic dan een beetje hijack.

[ Voor 11% gewijzigd door Soultaker op 23-02-2004 01:50 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 01:00

.oisyn

Moderator Devschuur®

Demotivational Speaker

Soultaker schreef op 23 februari 2004 @ 01:49:
De reden van de problemen lijkt me dat een enum-declaratie niet echt een zelfstandig type is, maar alleen wat constanten produceert. Dat is mijn speculatie; geen idee hoe dat in de standaarddocumentatie zit.
een enum definitie is wel degelijk een zelfstandig type als ie niet anonymous is. Je kunt ook geen integral type aan een enum assignen zonder expliciet te casten. Je kunt een enum als template parameter specificeren (een int gebruiken bij instantiatie geeft dan ook een error), en het wordt meegenomen bij function overloading

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

enum E
{
    x,
    y
};

std::ostream & operator << (std::ostream & o, E e)
{
    return o << (e == x ? 'x' : 'y');
}

int main ()
{
    E e = x;
    std::cout << e << std::endl;
    std::cout << y << std::endl;
    std::cout << (int)e << std::endl;
    return 0;
}


Bovenstaand programmaatje geeft bij mij de output
x
y
0
zoals je zou verwachten (VC++ 7.1)

Het probleem is volgens mij meer dat ie de template niet goed matcht.
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

template <class T>
struct S
{
    enum E { x, y };
};

template <class T>
void f (typename S<T>::E e)
{
    std::cout << e << std::endl;
}


int main ()
{
    f (S<void>::x);
}


Hierbij krijg ik de volgende compile error
code:
1
2
3
test4.cpp(19) : error C2783: 'void f(S<T>::E)' : could not deduce template
argument for 'T'
        test4.cpp(11) : see declaration of 'f'


Comeau zegt:
code:
1
2
3
4
"ComeauTest.c", line 19: error: no instance of function template "f" matches the
          argument list
            The argument types that you used are: (S<void>::E)
    f (S<void>::x);


even zoeken wat de standaard hierover zegt

[nohtml].edit: ah kijk, 14.8.2.4
-4- The nondeduced contexts are:
  • The nested-name-specifier of a type that was specified using a qualified-id.
  • A type that is a template-id in which one or more of the template-arguments is an expression that references a template-parameter.
  • When a type name is specified in a way that includes a nondeduced context, all of the types that comprise that type name are also nondeduced. However, a compound type can include both deduced and nondeduced types. [Example: If a type is specified as A<T>::B<T2>, both T and T2 are nondeduced. Likewise, if a type is specified as A<I+J>::X<T>, I, J, and T are nondeduced. If a type is specified as void f(A<T>::B, A<T>), the T in A<T>::B is nondeduced but the T in A<T> is deduced. ]

[ Voor 59% gewijzigd door .oisyn op 23-02-2004 15:41 ]

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.


  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 102% gewijzigd door Eelis op 18-02-2015 19:36 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 01:00

.oisyn

Moderator Devschuur®

Demotivational Speaker

Een instantiatie doet precies dat: instantieren. Dat heeft verder niets van doen bij het opzoeken van mogelijke functies. Zelfs explicit specialization werkt niet, omdat ie pas weet welke specialisatie hij moet pakken op het moment dat ie het type gededuceerd heeft

Het enige wat werkt is een "expliciete specialisatie" door een non-template overload van f te maken die gewoon een S<V>::E accepteert, maar daar schiet je natuurlijk weinig mee op

[ Voor 28% gewijzigd door .oisyn op 23-02-2004 15:40 ]

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.


  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 149% gewijzigd door Eelis op 18-02-2015 19:36 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 01:00

.oisyn

Moderator Devschuur®

Demotivational Speaker

Da's idd een oplossing, alleen vervelend dat je dan van die vage errors krijgt bij het aanroepen van een operator << op een ostream en een type waarvoor ie eigenlijk niet gedefineerd is :)

Maar ik vind het eigenlijk ook raar. Als die E in S een typedef was geweest dan kan ik dat nog begrijpen: dan zal ie moeten backtracken over alle mogelijke specialisaties van S om te kijken of er toevallig in een van de S'en een typedef staat die klopt. Maar het gegeven type is gewoon een S<V>::E, dan moet ie daar toch ook direct de T=V uit af kunnen leiden :?

[ Voor 1% gewijzigd door .oisyn op 23-02-2004 18:22 . Reden: klote html-rechten :( ]

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.


  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 111% gewijzigd door Eelis op 18-02-2015 19:36 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
.oisyn schreef op 23 februari 2004 @ 18:21:
Da's idd een oplossing, alleen vervelend dat je dan van die vage errors krijgt bij het aanroepen van een operator << op een ostream en een type waarvoor ie eigenlijk niet gedefineerd is :)

Maar ik vind het eigenlijk ook raar. Als die E in S een typedef was geweest dan kan ik dat nog begrijpen: dan zal ie moeten backtracken over alle mogelijke specialisaties van S om te kijken of er toevallig in een van de S'en een typedef staat die klopt. Maar het gegeven type is gewoon een S<V>::E, dan moet ie daar toch ook direct de T=V uit af kunnen leiden :?
Nee:
code:
1
2
3
4
template<>
class S<int> {
  typedef S<float>::E E;
};

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


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Eelis schreef op 23 februari 2004 @ 00:45:
Stel ik heb de volgende class template:
C++:
1
2
3
template <typename T> struct S {
  enum E { a = T::x, b };
};

En stel vervolgens dat ik voor die E een operator<< wil definiëren die in plaats van de integer representaties netjes 'a' en 'b' naar een ostream streamed, en die ik aan wil kunnen roepen met:
C++:
1
2
3
4
5
6
7
8
9
#include <iostream>

struct V { static int const x = 9; };

int main () {
  S<V>::E e = S<V>::b;
  std::cout << e;
  return 0;
}
Hoe moet dat dan?

Ik heb het volgende reeds geprobeerd:
C++:
1
2
3
4
template <typename T>
std::ostream & operator<< (std::ostream & o, typename S<T>::E e) {
  return o << (e == S<T>::a ? 'a' : 'b');
}
Dit compileerde wel, maar de operator werd niet aangeroepen (de integer representatie werd gestreamed). Kennelijk lukt het de compiler (gcc 3.2.3) niet het E type te matchen.

Is er een andere manier om zo'n operator te definiëren die wel goed werkt? Wie helpt?
Dit is een open issue op de standaard; er is geen simpele oplossing voor. Waarschijnlijk moet je wachten tot C++0x. Nou is de vraag hoe de verschillende operator<<s afhangen van T, is een typedef geen mogelijkheid?

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


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 01:00

.oisyn

Moderator Devschuur®

Demotivational Speaker

Hmm ik kende SFINAE niet, maar even googlen bracht me op het principe ervan. Wist helemaal niet dat dat bestond binnen C++ eigenlijk :)

Heb je daar meer info over?

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.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 01:00

.oisyn

Moderator Devschuur®

Demotivational Speaker

MSalters schreef op 23 februari 2004 @ 21:14:
[...]

Nee:
code:
1
2
3
4
template<>
class S<int> {
  typedef S<float>::E E;
};
ja maar dan is het een typedef, en dus in werkelijkheid gewoon een S::E, waardoor het als T=float gededuceerd wordt.

[nohtml]typedefs zijn slechts aliases voor het werkelijke type. Het werkelijke type is in het voorbeeld van Eelis gewoon S::E. Als dat een typedef zou zijn naar een Q::F, dan is dat het werkelijke type (en matcht ie de template functie logischerwijs ook niet). Ik bedoel, uiteindelijk komt het allemaal neer op 1 enkel type, wat evt. gematched zou kunnen worden. Misschien zie ik iets over het hoofd hoor, ik heb nog nooit een C++ compiler geimplementeerd en Bjarne & co hebben er vast goed over nagedacht, maar momenteel zie ik nog niet helemaal in waarom het niet zou kunnen :)

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.


  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 99% gewijzigd door Eelis op 18-02-2015 19:36 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Eelis schreef op 24 februari 2004 @ 00:18:
[...]
Hmm, puur uit nieuwsgierigheid: welke nieuwe features of wijzigingen in C++0x gaan dit oplossen?
Regels voor het overloaden van functies in namespace std, plus regels voor het gebruik van std:: functies door andere std::functies, plus misschien partiele functie specialisatie. Op het moment mag je in std:: alleen bestaande templates specialiseren, en functie templates kun je niet partieel specialiseren, dus een partiele specialisatie voor std::operator<< is onmogelijk.

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


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
.oisyn schreef op 23 februari 2004 @ 22:26:
[...]
ja maar dan is het een typedef, en dus in werkelijkheid gewoon een S<float>::E, waardoor het als T=float gededuceerd wordt.
Yep - is dat geen probleem, als je een S<int>::E hebt gebruikt?

Evengoed:
code:
1
2
3
4
5
6
7
8
9
class S_base {
  enum E { a, b };
};
template< typename T >
class S : public S_base {
};
// Specialisaties met separate S<T>::E
template <typename T>
std::ostream& operator<< ( std::ostream&, S<T>::E );

Overigens zijn dit soort testcases makkelijker helder te krijgen met een non-type parameter:
code:
1
2
3
4
template< int N >
class S {
  enum E { SN = N };
};

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


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 01:00

.oisyn

Moderator Devschuur®

Demotivational Speaker

MSalters schreef op 24 februari 2004 @ 12:22:
[...]

Yep - is dat geen probleem, als je een S<int>::E hebt gebruikt?
nou ja mijn inziens niet, maar je hebt gelijk dat dat wel nogal verwarrend kan zijn natuurlijk. Maar met je laatste voorbeeld zie ik idd in dat het een probleem is.

Komen er in C++0x eigenlijk eindelijk eens template typedefs?

offtopic:
.edit: dat komt door mijn html rechten :( als ik wil dat de < en > worden geconverteerd naar < en >, dan moet ik er [rml][ nohtml][/rml] omheen zetten. Heel irritant als je het mij vraagt, liever andersom: als je html wil posten moet je [rml][ html][/rml] gebruiken. Dat staat al tijden op de TODO list van React maar het schijnt er maar niet van te komen. Er zijn ook wat problemen met oudere posts wat dat betreft (hoe weet je of de persoon in kwestie op moment van posten html rechten had?)

Standaard worden alle tags, ookal herkent de browser ze niet, gewoon niet getoond overigens, vandaar dat jouw <int> wegviel in de quote hierboven

[ Voor 55% gewijzigd door .oisyn op 24-02-2004 19:13 ]

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.


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Waarschijnlijk wel, de allocator rebind hack was al vrij overtuigend.
offtopic:
Sinds wanneer is <float> een html tag?

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

Pagina: 1