conversie implicita de la int la enum

Intrebari despre limbajul C++, standardul C++, STL, OOP in C++ sau alte subiecte nelegate de VisualC++
User avatar
dumion
Membru
Membru
Posts: 74
Joined: 25 Jul 2007, 10:17

conversie implicita de la int la enum

Post by dumion » 12 Mar 2008, 12:59

Sa zicem ca am enum ca mai jos folosit intr-o structura:

Code: Select all

enum Flags
{
   One = 1, 
   Two = 2,
   Tree = 4
};

struct foo
{
   int      m_Data;
   Flags    m_Flags;

   foo(int data, Flags flags):
      m_Data(data), m_Flags(flags)
      {
      }
};
Acuma am o metoda care creaza un foo, cam asa:

Code: Select all

void CreateFoo(int flags)
{
   foo f(100, flags);
}

int _tmain(int argc, _TCHAR* argv[])
{
   CreateFoo(1|2);

   return 0;
}
Compilatorul imi da urmatoarea eroare:
error C2664: 'foo::foo(int,Flags)' : cannot convert parameter 2 from 'int' to 'Flags'
Conversion to enumeration type requires an explicit cast (static_cast, C-style cast or function-style cast)
Se poate face cumva ca aceasta convesie sa fie implicita?



User avatar
Silviu Ardelean
Senior
Senior
Posts: 1175
Joined: 12 Jul 2007, 09:22
Judet: Timiş
Location: Timisoara
Contact:

Re: conversie implicita de la int la enum

Post by Silviu Ardelean » 12 Mar 2008, 13:41

dumion wrote: int _tmain(int argc, _TCHAR* argv[])
{
CreateFoo(1|2);

return 0;
}
[/code]
Compilatorul imi da urmatoarea eroare:
error C2664: 'foo::foo(int,Flags)' : cannot convert parameter 2 from 'int' to 'Flags'
Conversion to enumeration type requires an explicit cast (static_cast, C-style cast or function-style cast)
Se poate face cumva ca aceasta convesie sa fie implicita?
Eroarea ce-ti apare tie e fireasca. Tu creezi un o instanta si in loc de o var. de tip Flags tu trimiti la acel constructor un int.
Metoda CreateFoo() ar trebui sa arate asa:

Code: Select all

void CreateFoo(Flags flags)
{
	foo f(100, flags);
}
Apoi, nu uita ca pana la urma, elementele unui enum sunt de fapt niste constante intregi.
Cand apelezi CreateFoo() in main() trebuie sa transmiti un element din enum-ul Flags nu numere intregi si cu atat mai putzin operatii pe biti intre acestea.

Code: Select all

CreateFoo(Two);
Daca vrei sa faci operatii cu biti, se face cam asa:

Code: Select all

                int x1 = 1;
	int x2 = 2;
	int x3 = x1|x2;
In acest caz x3 = 3. Totusi, compilatorul nu-ti permite sa transmiti decat constante din enum. Inchipuie-ti ce s-ar intampla oricare din var. x1 sau x2 ar avea alte valori. Ce s-ar intampla daca de ex. tu transmiti in locul unei constante un nr. oarecare... la intamplare? Valoarea lui x3 va fi un nr. inexistent in acel enum.

Si ca o ultima observatie, esti sigur ca in enum-ul tau "Tree = 4"? E drept ca se poate... dar asocierea ta e ambigua.

User avatar
dumion
Membru
Membru
Posts: 74
Joined: 25 Jul 2007, 10:17

Re: conversie implicita de la int la enum

Post by dumion » 12 Mar 2008, 14:21

Mersi pentru raspuns, dar n-ai inteles. Eu am incercat sa dau un exemplu simplu care sa reflecte problema. Daca vrei sa te lemuresc mai aproape de problema mea, o sa incerc sa simplific.

Am un enum care defineste comenzi posibile pentru utilizator. (Simplificat) sa zicem ca ar fi:

Code: Select all

enum CommandType
{
   None = 0,
   Connect,
   Disconnect,
   Reset,
   // alte comenzi
};
Acuma am o clasa care reprezinta o comanda, si printre altele contine si tipul comenzii. Simplicat, sa zicem ca arata asa:

Code: Select all

class Command
{
   CommandType m_type;
   // alte date

public:
   Command(CommandType type): m_type(type)
   {
   }
}
Acuma, smecheria e ca eu aceste comenzi, Connect, Disconnect, Reset, etc. le am intr-un meniu. Fiecare comanda e un item. Cand selectez o comanda din meniu, trebuie sa construiect un astfel de obiect Command. Si eu stiu id-ul item-ului de meniu si vreau sa-l transform intr-un astfel de CommandType.

Code: Select all

Command* cmd = new Command(id - ID_COMMANDS_CONNECT);
Sper ca acuma e mai clar...

User avatar
Silviu Ardelean
Senior
Senior
Posts: 1175
Joined: 12 Jul 2007, 09:22
Judet: Timiş
Location: Timisoara
Contact:

Re: conversie implicita de la int la enum

Post by Silviu Ardelean » 12 Mar 2008, 14:33

dumion wrote:Mersi pentru raspuns, dar n-ai inteles. Eu am incercat sa dau un exemplu simplu care sa reflecte problema. Daca vrei sa te lemuresc mai aproape de problema mea, o sa incerc sa simplific.
Scuza-ma dar eu am inteles exact problema postata de tine... care oricum era gresit implementata. Acum, tu vi cu problema altfel pusa... ceva concret, dar de fapt te referi la acelasi lucru. Vezi comentariile de mai jos.
dumion wrote: Am un enum care defineste comenzi posibile pentru utilizator. (Simplificat) sa zicem ca ar fi:

Code: Select all

enum CommandType
{
   None = 0,
   Connect,
   Disconnect,
   Reset,
   // alte comenzi
};
Acuma am o clasa care reprezinta o comanda, si printre altele contine si tipul comenzii. Simplicat, sa zicem ca arata asa:

Code: Select all

class Command
{
   CommandType m_type;
   // alte date

public:
   Command(CommandType type): m_type(type)
   {
   }
}
Acuma, smecheria e ca eu aceste comenzi, Connect, Disconnect, Reset, etc. le am intr-un meniu. Fiecare comanda e un item. Cand selectez o comanda din meniu, trebuie sa construiect un astfel de obiect Command. Si eu stiu id-ul item-ului de meniu si vreau sa-l transform intr-un astfel de CommandType.

Code: Select all

Command* cmd = new Command(id - ID_COMMANDS_CONNECT);
Sper ca acuma e mai clar...
Eu nu inteleg de ce te cramponezii sa foloesti enum si transmiti un obiect enum.
Nu uita ca acel ID din menu e de fapt un #define si tu de fapt faci constanta minus alta constanta. Unde e elementul din enum-ul tau? :?
Faci aceeasi greseala ca mai sus. Man, la tine ID-urile nu sunt acelasi lucru cu ce ai in enum, chiar daca tu faci aceasta asociere.
Nu e acelasi lucru.

Viorel
Microsoft MVP
Microsoft MVP
Posts: 293
Joined: 13 Jul 2007, 12:26

Re: conversie implicita de la int la enum

Post by Viorel » 12 Mar 2008, 14:56

Probabil un alt constructor ar rezolva problema:

Code: Select all

enum CommandType
{
   None = 0,
   . . .
   _Last
};

class Command
{
    . . .
    explicit Command(int type) : m_type(CommandType(type))
    {
        assert(type > CommandType::None && type < CommandType::_Last);
    }
};

User avatar
dumion
Membru
Membru
Posts: 74
Joined: 25 Jul 2007, 10:17

Re: conversie implicita de la int la enum

Post by dumion » 12 Mar 2008, 15:12

Silviu Ardelean wrote:Eu nu inteleg de ce te cramponezii sa foloesti enum si transmiti un obiect enum.
Pentru ca e mai frumos asa. Mai OO.
Silviu Ardelean wrote:Nu uita ca acel ID din menu e de fapt un #define si tu de fapt faci constanta minus alta constanta. Unde e elementul din enum-ul tau?
Pai la fiecare constanta din enum ii corespunde un item in meniu, in aceiasi ordine. Deci pot sa transform din unul in altul.
Silviu Ardelean wrote:Faci aceeasi greseala ca mai sus. Man, la tine ID-urile nu sunt acelasi lucru cu ce ai in enum, chiar daca tu faci aceasta asociere.
Nu e acelasi lucru.
Pai eu nu cred ca fac nici o gresala. Atat timp cat exista o relatie 1 la 1 intre cele 2, nu vad de ce nu as putea converti asa cum iti aratam.
Viorel wrote:Probabil un alt constructor ar rezolva problema.
Aha. Mersi, o sa incerc.

User avatar
Silviu Ardelean
Senior
Senior
Posts: 1175
Joined: 12 Jul 2007, 09:22
Judet: Timiş
Location: Timisoara
Contact:

Re: conversie implicita de la int la enum

Post by Silviu Ardelean » 12 Mar 2008, 15:30

dumion wrote:
Silviu Ardelean wrote:Eu nu inteleg de ce te cramponezii sa foloesti enum si transmiti un obiect enum.
Pentru ca e mai frumos asa. Mai OO.
E bine sa nu abuzam de OOP daca nu e nevoie. ;) Exista situatii in care, a abuza de OOP, e ca si cum te-ai scarpina in urecea stanga cu mana dreapta.
dumion wrote:
Silviu Ardelean wrote:Nu uita ca acel ID din menu e de fapt un #define si tu de fapt faci constanta minus alta constanta. Unde e elementul din enum-ul tau?
Pai la fiecare constanta din enum ii corespunde un item in meniu, in aceiasi ordine. Deci pot sa transform din unul in altul.
Esti sigur ca asocierea ta e safe? Si ca sa-ti dau ex. de ce zic ca nu e safe uita-te la chestia asta:

Code: Select all

int x1 = 3;
int x2 = 5;
int x3 = x1|x2;
CreateFoo(static_cast<Flags>(x3));
Cast-ul merge pe ambele exemple, inclusiv in clasa ta (solutie alternativa la propunerea lui Viorel - dar non-safe). Dar x3 = 7 care nu se regaseste in enum-ul tau.
Deci, cu cine asociezi, matale? :twisted:
dumion wrote:
Silviu Ardelean wrote:Faci aceeasi greseala ca mai sus. Man, la tine ID-urile nu sunt acelasi lucru cu ce ai in enum, chiar daca tu faci aceasta asociere.
Nu e acelasi lucru.
Pai eu nu cred ca fac nici o gresala. Atat timp cat exista o relatie 1 la 1 intre cele 2, nu vad de ce nu as putea converti asa cum iti aratam.
Eroarea mentionata la inceput (error C2664)e generata ptr. ca tu nu faci greseli?

Exemplul de mai sus iti arata si de ce asocierea ta nu e SAFE. :!: Cred ca stii, de aceea a pus Viorel, assert-ul acela in constructor. ;)

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: conversie implicita de la int la enum

Post by Ovidiu Cucu » 12 Mar 2008, 16:15

Scuze ca ma bag si eu cu o mica observatie
  • Atunci cand eram mai mititel [ :D ] faceam si eu greseala sa ma bizui pe ID-uri consecutive. Acuma ma gandesc mai intai: dar daca cineva, din anumite motive umbla in resource.h si le modifica si sau sterge unul? dar daca apar item-uri noi? dar daca.... ?...
Asa ca, un pic mult mai bine as scrie codul cam asa

Code: Select all

   CommandType type = None;

   switch(id)
   {
   case ID_COMMANDS_CONNECT:
      type = Connect;
      break;
   case ID_COMMANDS_DISCONNECT:
      type = Disconnect;
      break;
   case ID_COMMANDS_RESET:
      type = Reset;
      break;
   // ... alte commenzi
   default:
      type = None; // <-- or better, put an ASSERT
      break;   
   }

   Command* cmd = new Command(type);
Am evitat astfel si problemele descrise mai sus si conversia de la "numere magice" la enum.
Nota importanta: enum-ul a fost inventat tocmai pentru a scapa de numere magice batute-n cod.

User avatar
dumion
Membru
Membru
Posts: 74
Joined: 25 Jul 2007, 10:17

Re: conversie implicita de la int la enum

Post by dumion » 12 Mar 2008, 16:21

Silviu, cred ca tu te cramponezi pe linia asta de cod:

Code: Select all

CreateFoo(1|2);
Te rog ignor-o. A fost un exemplu care replica problema. Eu la mine in cod nu fac Command(Connect|Disconnect).
Silviu Ardelean wrote:Eroarea mentionata la inceput (error C2664)e generata ptr. ca tu nu faci greseli?
Pai sigur ca nu din cauza unei greseli de a mea e generata. E generata pentru ca acea conversie implicita nu e permisa de limbaj. Si asta voiam sa aflu, daca se exista un mechanism care sa permita asta sau nu.

Ce a spus Viorel merge excelent, pentru initializarea unui obiect command. Dar daca vreau sa fac asa:

Code: Select all

CommandType type = id - ID_COMMANDS_CONNECT;
imi da aceiasi eroare.

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: conversie implicita de la int la enum

Post by Ovidiu Cucu » 12 Mar 2008, 16:36

dumion wrote:[...]
Ce a spus Viorel merge excelent, pentru initializarea unui obiect command [...]
Bineinteles ca merge, dar strica munca unui omuletz care s-a chinuit sa scrie un enum.

BTW. Trebuie sa-mi editez "scrisorica" mea de mai sus si sa pun fontul mai mare? ;)

User avatar
Silviu Ardelean
Senior
Senior
Posts: 1175
Joined: 12 Jul 2007, 09:22
Judet: Timiş
Location: Timisoara
Contact:

Re: conversie implicita de la int la enum

Post by Silviu Ardelean » 12 Mar 2008, 16:37

dumion wrote:Silviu, cred ca tu te cramponezi pe linia asta de cod:

Code: Select all

CreateFoo(1|2);
Te rog ignor-o. A fost un exemplu care replica problema. Eu la mine in cod nu fac Command(Connect|Disconnect).
Man, eu doar vroiam sa-ti arat de ce nu e safe si m-am folosit de acel exemplu.
dumion wrote: Ce a spus Viorel merge excelent, pentru initializarea unui obiect command. Dar daca vreau sa fac asa:

Code: Select all

CommandType type = id - ID_COMMANDS_CONNECT;
imi da aceiasi eroare.
Sincer, nu ma mira. Poti incerca si cu static_cast in combinatie cu observatia f. buna a lui Ovidiu. ;)

Dragos Cojocari
Membru++
Membru++
Posts: 789
Joined: 11 Jul 2007, 14:11

Re: conversie implicita de la int la enum

Post by Dragos Cojocari » 12 Mar 2008, 16:38

Poti asa:

Code: Select all

CommandType type = (CommandType) id - ID_COMMANDS_CONNECT;
Dar in acest mod nimic nu garanteaza ca int-ul e in range-ul specificat de enum.

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: conversie implicita de la int la enum

Post by Ovidiu Cucu » 12 Mar 2008, 16:41

Silviu Ardelean wrote:Poti incerca si cu static_cast in combinatie cu observatia f. buna a lui Ovidiu. ;)
Nuuuuuu! Va rog, NU COMBINATI OBSERVATIA MEA CU NICI UN FEL DE CAST!

User avatar
Silviu Ardelean
Senior
Senior
Posts: 1175
Joined: 12 Jul 2007, 09:22
Judet: Timiş
Location: Timisoara
Contact:

Re: conversie implicita de la int la enum

Post by Silviu Ardelean » 12 Mar 2008, 16:51

Ovidiu Cucu wrote:
Silviu Ardelean wrote:Poti incerca si cu static_cast in combinatie cu observatia f. buna a lui Ovidiu. ;)
Nuuuuuu! Va rog, NU COMBINATI OBSERVATIA MEA CU NICI UN FEL DE CAST!
Scuze, m-am lasat dus de val si am mers prea departe. 8-)
Ma gandeam la cast pe ID... dar nu e cazul. E bine asa cum e in ex. cu switch, fara cast. :thumbsup:

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: conversie implicita de la int la enum

Post by Ovidiu Cucu » 12 Mar 2008, 17:09

Acuma, gadiland putin la OO-ul de care se face atata caz in ziua de azi, toata lumea il cunoaste (pe de rost), dar foarte putini il aplica (in practica) asa cum trebuie:

N-ar fi oare mai eleganta o clasa (abstracta) Command din care deriva Connect, Disconnect, Reset, s.a.m.d?
Eu cred ca da. In plus scapi si de acel enum "enervant" si de alte cosmaruri care altfel ar urma daca implementezi clasa Command_cea_buna_la_de_toate. ;)

Post Reply