Što znači eksplicitna ključna riječ?

Što znači explicit ključna riječ u C ++?

2387
23 сент. postavila Skizz 23. rujna 2008-09-23 16:58 '08 u 16:58 2008-09-23 16:58
@ 11 odgovora

Kompajleru je dopušteno izvesti jednu implicitnu konverziju za rješavanje parametara funkcije. To znači da prevodilac može koristiti konstruktore s jednim parametrom za konverziju iz jednog tipa u drugi da bi dobio ispravan tip za parametar.

Ovdje je primjer klase s konstruktorom koji se može koristiti za implicitne pretvorbe:

 class Foo { public: // single parameter constructor, can be used as an implicit conversion Foo (int foo) : m_foo (foo) { } int GetFoo () { return m_foo; } private: int m_foo; }; 

Ovdje je jednostavna funkcija koja prihvaća Foo objekt:

 void DoBar (Foo foo) { int i = foo.GetFoo (); } 

i ovdje, gdje se DoBar funkcija.

 int main () { DoBar (42); } 

Argument nije Foo objekt, ali << 26>. Međutim, postoji konstruktor za Foo , koji uzima int , tako da se ovaj konstruktor može koristiti za pretvaranje parametra u ispravan tip.

Kompajleru je dopušteno da to učini jednom za svaki parametar.

Prefiks ključne riječi, explicit konstruktoru, sprječava prevodilac da koristi ovaj konstruktor za implicitne pretvorbe. Dodavanje u klasu iznad rezultirat će pogreškom kompajlera prilikom pozivanja funkcije DoBar (42) . Sada morate izričito nazvati pretvorbu pomoću DoBar (Foo (42))

Razlog zbog kojeg to želite učiniti je izbjegavanje slučajne konstrukcije koja može sakriti pogreške. Promišljen primjer:

  • Imate klasu MyString(int size) s konstruktorom koji gradi niz zadane veličine. Imate funkciju print(const MyString> i pozivate print(3) (kada ste zapravo namjeravali nazvati print("3") ). Očekujete da će ispisati "3", ali umjesto toga ispisuje prazan niz duljine 3.
2778
23 сент. odgovor se daje Skizzu 23. rujna. 2008-09-23 16:59 '08 u 16:59 2008-09-23 16:59

Pretpostavimo da imate klasu String :

 class String { public: String(int n); // allocate n bytes to the String object String(const char *p); // initializes object with char *p }; 

Sada, ako pokušate:

border=0
 String mystring = 'x'; 

Znak 'x' implicitno će se pretvoriti u int , a zatim će se pozvati konstruktor String(int) . Ali to nije ono što bi korisnik mogao namjeravati. Dakle, da bismo spriječili takve uvjete, definirat ćemo konstruktora kao explicit :

 class String { public: explicit String (int n); //allocate n bytes String(const char *p); // initialize sobject with string p }; 
971
23 сент. odgovor je dao Eddie 23. rujna 2008-09-23 17:09 '08 u 17:09 2008-09-23 17:09

U C ++, konstruktor s jednim potrebnim parametrom se smatra implicitnom funkcijom konverzije. Ona pretvara tip parametra u tip klase. Je li to dobra stvar ili ne ovisi o semantici konstruktora.

Na primjer, ako imate string klasu s konstruktom String(const char* s) , možda je to ono što želite. Možete proslijediti const char* funkciju koja očekuje String , a prevoditelj će automatski stvoriti privremeni objekt String za vas.

S druge strane, ako imate klasu međuspremnika, konstruktor Buffer(int size) uzima veličinu međuspremnika u bajtovima, vjerojatno ne želite da kompajler lako pretvori int u Buffer s. Da biste to spriječili, deklarirate konstruktora s explicit ključnom riječi:

 class Buffer { explicit Buffer(int size); ... } 

Dakle,

 void useBuffer(Buffer buf); useBuffer(4); 

postaje pogreška vremena prevođenja. Ako želite proslijediti privremeni objekt Buffer , morate to učiniti izričito:

 useBuffer(Buffer(4)); 

Dakle, ako vaš konstruktor s jednim parametrom pretvara parametar u objekt vaše klase, vjerojatno ne želite koristiti ključnu riječ explicit . Ali ako imate konstruktora koji uzima samo jedan parametar, morate ga deklarirati kao explicit , tako da vas prevoditelj ne iznenađuje neočekivanim pretvorbama.

133
23 сент. odgovor je dan cjm 23 sep. 2008-09-23 19:37 '08 u 19:37 2008-09-23 19:37

Taj se odgovor odnosi na stvaranje objekta s / bez eksplicitnog konstruktora, jer se on ne razmatra u drugim odgovorima.

Razmotrite sljedeću klasu bez eksplicitnog konstruktora:

 class Foo { public: Foo(int x) : m_x(x) { } private: int m_x; }; 

Objekti klase Foo mogu se izraditi na dva načina:

 Foo bar1(10); Foo bar2 = 20; 

Ovisno o implementaciji, drugi način stvaranja instance Foo klase može biti zbunjujući ili ne ono što programer planira. Foo bar2 = 20; prefiks ključne riječi za konstruktora generira Foo bar2 = 20; pogrešku u Foo bar2 = 20; ,

Obično je dobra praksa proglasiti konstruktore jednim argumentom kao explicit , osim ako ga vaša provedba to zabranjuje.

Imajte na umu i konstruktore s

  • zadane argumente za sve parametre ili
  • zadani argumenti za drugi parametar

oba se mogu koristiti kao konstruktori s jednim argumentom. Stoga to možete učiniti explicit .

Na primjer, ako namjerno ne želite da vaš konstruktor s jednim argumentom bude eksplicitan, to je ako stvorite functor (pogledajte strukturu add_x deklariranu u ovom odgovoru). U ovom slučaju objekt se kreira kao add_x add30 = 30; vjerojatno ima smisla.

Ovdje je dobar zapis eksplicitnih konstruktora.

34
08 окт. Odgovori Gautam Oct 08. 2013-10-08 17:43 '13 u 17:43 2013-10-08 17:43

Ključna riječ explicit čini konstruktora konverzije konstruktorom koji nije pretvoriv. Kao rezultat, kod je manje sklon pogreškama.

32
21 нояб. odgovor je dao SankararaoMajji 21. studenog. 2012-11-21 05:36 '12 u 5:36 am 2012-11-21 05:36

Ključna riječ explicit prati također

  • konstruktor klase X, koji se ne može koristiti za implicitno pretvaranje prvog (bilo kojeg samo) parametra u tip X

C ++ [class.conv.ctor]

1) Konstruktor deklariran bez eksplicitnog specifičara funkcije specificira pretvorbu iz tipova svojih parametara u tip njegove klase. Takav se konstruktor naziva konstruktor transformacije.

2) Izričiti konstruktor konstruira objekte na isti način kao i implicitni konstruktori, ali to čini samo tamo gdje se eksplicitno koristi sintaksa izravne inicijalizacije (8.5) ili gdje se koriste bacanja (5.2.9, 5.4). Zadani konstruktor može biti eksplicitni konstruktor; takav će se konstruktor koristiti za inicijalizaciju po defaultu ili inicijalizaciju vrijednosti (8.5).

  • ili funkciju konverzije, koja se razmatra samo za izravnu inicijalizaciju i eksplicitnu pretvorbu.

C ++ [class.conv.fct]

2) Funkcija transformacije može biti eksplicitna (7.1.2), u kojem slučaju se smatra samo kao prilagođena transformacija za izravnu inicijalizaciju (8.5). U suprotnom, korisnički definirane transformacije nisu ograničene na dodjelu i inicijalizaciju.

pregled

Eksplicitne funkcije transformacije i konstruktori mogu se koristiti samo za eksplicitne transformacije (direktna inicijalizacija ili eksplicitna operacija lijevanja), dok se implicitni konstruktori i funkcije transformacije mogu koristiti za eksplicitne i eksplicitne transformacije.

  

Primjer korištenja struktura X, Y, Z i funkcija foo, bar, baz :

Pogledajte malu konfiguraciju struktura i funkcija da biste vidjeli razliku između explicit i ne- explicit .

 struct Z { }; struct X { explicit X(int a); // X can be constructed from int explicitly explicit operator Z (); // X can be converted to Z explicitly }; struct Y{ Y(int a); // int can be implicitly converted to Y operator Z (); // Y can be implicitly converted to Z }; void foo(X x) { } void bar(Y y) { } void baz(Z z) { } 

Primjeri dizajnera:

Konverzija argumenta funkcije:

 foo(2); // error: no implicit conversion int to X possible foo(X(2)); // OK: direct initialization: explicit conversion foo(static_cast<X>(2)); // OK: explicit conversion bar(2); // OK: implicit conversion via Y(int) bar(Y(2)); // OK: direct initialization bar(static_cast<Y>(2)); // OK: explicit conversion 

Inicijalizacija objekta:

 X x2 = 2; // error: no implicit conversion int to X possible X x3(2); // OK: direct initialization X x4 = X(2); // OK: direct initialization X x5 = static_cast<X>(2); // OK: explicit conversion Y y2 = 2; // OK: implicit conversion via Y(int) Y y3(2); // OK: direct initialization Y y4 = Y(2); // OK: direct initialization Y y5 = static_cast<Y>(2); // OK: explicit conversion 

Primjeri funkcija pretvorbe:

 X x1{ 0 }; Y y1{ 0 }; 

Konverzija argumenta funkcije:

 baz(x1); // error: X not implicitly convertible to Z baz(Z(x1)); // OK: explicit initialization baz(static_cast<Z>(x1)); // OK: explicit conversion baz(y1); // OK: implicit conversion via Y::operator Z() baz(Z(y1)); // OK: direct initialization baz(static_cast<Z>(y1)); // OK: explicit conversion 

Inicijalizacija objekta:

 Z z1 = x1; // error: X not implicitly convertible to Z Z z2(x1); // OK: explicit initialization Z z3 = Z(x1); // OK: explicit initialization Z z4 = static_cast<Z>(x1); // OK: explicit conversion Z z1 = y1; // OK: implicit conversion via Y::operator Z() Z z2(y1); // OK: direct initialization Z z3 = Z(y1); // OK: direct initialization Z z4 = static_cast<Z>(y1); // OK: explicit conversion 

Zašto koristiti funkcije pretvorbe ili explicit konstruktore?

Konstruktori konverzije i funkcije implicitne konverzije mogu uvesti dvosmislenost.

Razmotrimo strukturu V pretvorenu u int , strukturu U implicitno konstruktivnu od V i funkciju f preopterećenu za U i bool .

 struct V { operator bool() const { return true; } }; struct U { U(V) { } }; void f(U) { } void f(bool) { } 

Poziv f dvosmislen pri prolasku objekta tipa V

 V x; f(x); // error: call of overloaded 'f(V is ambiguous 

Kompajler ne zna hoće li koristiti konstruktor U ili funkciju konverzije za pretvaranje V objekta u tip za prijelaz na f .

Ako je ili konstruktor U , ili funkcija konverzije V explicit , tada neće biti dvosmislenosti, jer će se uzeti u obzir samo implicitna konverzija. Ako su oba eksplicitna, pozivanje f pomoću objekta tipa V mora se izvršiti pomoću eksplicitne konverzije ili operacije lijevanja.

Konstruktori konverzije i funkcije implicitne konverzije mogu dovesti do neočekivanog ponašanja.

Razmotrite funkciju ispisa vektora:

 void print_intvector(std::vector<int> const  { for (int x : v) std::cout << x << '\n'; } 

Ako veličina vektorskog konstruktora ne bi bila eksplicitna, takva bi se funkcija mogla zvati:

 print_intvector(3); 

Što se može očekivati ​​od takvog poziva? Sadrži li jedan redak 3 ili tri retka koji sadrže 0 ? (Gdje se drugi dogodi, što se događa.)

Koristeći eksplicitnu ključnu riječ u sučelju razreda prisiljava korisničko sučelje da izričito navede željenu pretvorbu.

Kako piše Bjarne Straustrup (u C ++ programskom jeziku, 4. izdanje, 35.2.1, str. 1011), pitanje zašto std::duration ne može biti implicitno konstruirano iz prostog broja:

Ako znate na što mislite, budite iskreni.

31
11 июля '15 в 2:48 2015-07-11 02:48 Odgovor je dao Pixelchemist 11. srpnja 2009. u 2:48 2015-07-11 02:48

explicit ključna riječ može se upotrijebiti kako bi se konstruktor pozvao eksplicitno.

 class C{ public: explicit C(void) = default; }; int main(void){ C c(); return 0; } 

Eksplicitna -ključna riječ prije C(void) konstruktora C(void) govori prevodiocu da je dopušten samo eksplicitan poziv ovom konstruktoru.

Ključ explicit ključa može se koristiti iu prilagođenim operacijama konverzije tipa:

 class C{ public: explicit inline operator bool(void) const{ return true; } }; int main(void){ C c; bool b = static_cast<bool>(c); return 0; } 

Ovdje explicit ključna riječ primjenjuje samo eksplicitne bačve na radnju, dakle bool b = c; u ovom slučaju neće biti valjana. U takvim situacijama explicit ključna riječ može pomoći programeru da izbjegne implicitne, nenamjerne prenapone. Ova uporaba je standardizirana u C ++ 11 .

25
14 мая '13 в 12:28 2013-05-14 12:28 odgovor je dao Helixirr 14. svibnja '13 u 12:28 2013-05-14 12:28

O tome smo već razgovarali ( što je eksplicitni konstruktor ). Ali moram reći da mu nedostaju detaljni opisi koji se ovdje nalaze.

Osim toga, uvijek je dobra praksa kodiranja izrada vlastitih konstruktora argumenata (uključujući i one koji imaju zadane vrijednosti za arg2, arg3, ...), kao što je već rečeno. Kao i uvijek kod C ++-a: ako to ne učinite, htjet ćete da to učinite ...

Još jedna dobra praksa za klase je da kopiranje i dodjeljivanje privatnog (aka onesposobiti) ako ga stvarno ne morate implementirati. Time se izbjegavaju moguće kopije pokazivača kada se koriste metode koje C ++ stvara za vas prema zadanim postavkama. Drugi način da to učinite je da dobijete poticaj: noncopyable.

17
02 окт. odgovor je dat fmuecke 02 oct. 2009-10-02 01:00 '09 u 1:00 am 2009-10-02 01:00

Link Cpp je uvijek koristan !!! Pojedinosti o eksplicitnom specifikatoru možete pronaći ovdje . Možda ćete morati pogledati implicitne pretvorbe i inicijalizaciju kopiranja .

Brzi pogled

Izričiti specifikator označava da konstruktor ili funkcija konverzije (od C ++ 11) ne dopušta implicitnu konverziju ili inicijalizaciju kopije.

Primjer je sljedeći:

 struct A { A(int) { } // converting constructor A(int, int) { } // converting constructor (C++11) operator bool() const { return true; } }; struct B { explicit B(int) { } explicit B(int, int) { } explicit operator bool() const { return true; } }; int main() { A a1 = 1; // OK: copy-initialization selects A::A(int) A a2(2); // OK: direct-initialization selects A::A(int) A a3 {4, 5}; // OK: direct-list-initialization selects A::A(int, int) A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int) A a5 = (A)1; // OK: explicit cast performs static_cast if (a1) cout << "true" << endl; // OK: A::operator bool() bool na1 = a1; // OK: copy-initialization selects A::operator bool() bool na2 = static_cast<bool>(a1); // OK: static_cast performs direct-initialization // B b1 = 1; // error: copy-initialization does not consider B::B(int) B b2(2); // OK: direct-initialization selects B::B(int) B b3 {4, 5}; // OK: direct-list-initialization selects B::B(int, int) // B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int) B b5 = (B)1; // OK: explicit cast performs static_cast if (b5) cout << "true" << endl; // OK: B::operator bool() // bool nb1 = b2; // error: copy-initialization does not consider B::operator bool() bool nb2 = static_cast<bool>(b2); // OK: static_cast performs direct-initialization } 
15
20 авг. odgovor se daje selfboot 20 aug. 2016-08-20 15:45 '16 u 15:45 2016-08-20 15:45

Konstruktori eksplicitnih pretvorbi (samo C ++)

Izričiti specifikator funkcija kontrolira neželjeni implicitni tip pretvorbe. Može se koristiti samo u deklaracijama konstruktora u deklaraciji klase. Na primjer, osim konstruktora, konstruktori u sljedećoj klasi su transformatori konstruktora.

 class A { public: A(); A(int); A(const char*, int = 0); }; 

Sljedeći su oglasi zakoniti:

 A c = 1; A d = "Venditti"; 

Prva deklaracija je jednaka A c = A( 1 ); ,

Ako proglasite konstruktora klase explicit , prethodne izjave bi bile nezakonite.

Na primjer, ako proglasite klasu kao:

 class A { public: explicit A(); explicit A(int); explicit A(const char*, int = 0); }; 

Možete dodijeliti samo vrijednosti koje odgovaraju vrijednostima tipa klase.

Na primjer, sljedeće izjave su zakonite:

  A a1; A a2 = A(1); A a3(1); A a4 = A("Venditti"); A* p = new A(1); A a5 = (A)1; A a6 = static_cast<A>(1); 
10
20 дек. Odgovor se daje coding_ninza 20. prosinca. 2017-12-20 15:19 '17 u 15:19 2017-12-20 15:19