Soru Açık anahtar kelime ne anlama geliyor?


Ne yapar explicit anahtar kelime C ++ cinsinden mi?


2371
2017-09-23 13:58


Menşei


Sadece C ++ 11'den beri yeni gelen herkese işaret etmek istiyorum. explicit sadece kuruculardan daha fazlasına uygulanabilir. Artık dönüşüm operatörlerine uygulandığında geçerli. Bir dersin olduğunu söyle BigInt bir dönüşüm operatörü ile int ve açık bir dönüşüm operatörü std::string Sebebi ne olursa olsun. Söyleyebileceksiniz int i = myBigInt;ama açıkça kullanmanız gerekecek ( static_cast, tercihen) söylemek std::string s = myBigInt;. - chris
Açıkça belirtilemez, ayrıca atamaya da başvurabilir misiniz? (Yani int x(5);) - Eitan Myron
theunixshell.blogspot.com/2013/01/explicit-keyword-in-c.html - Vijay
@chris Açık bir örtük dönüşüm fikri saçmadır. Ondan uzak durun! - curiousguy
@curiousguy, Açık örtük bir dönüşüm diye bir şey yoktur. - chris


Cevaplar:


Derleyici, parametreleri bir işleve çözmek için bir örtük dönüştürme yapmasına izin verilir. Bunun anlamı, derleyicinin, tek parametre Bir parametre için doğru tip almak için bir türden diğerine dönüştürmek.

Burada, örtülü dönüşümler için kullanılabilecek bir kurucuya sahip bir örnek sınıf var:

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;
};

İşte basit bir işlev Foo nesne:

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

ve işte burada DoBar işlev denir.

int main ()
{
  DoBar (42);
}

Argüman bir değil Foo nesne, ama bir int. Ancak, bunun için bir kurucu var Foo bu bir alır int Böylece bu kurucu parametreyi doğru türe dönüştürmek için kullanılabilir.

Her parametre için derleyicinin bir kez yapmasına izin verilir.

Önek explicit Kurucuya anahtar sözcük, derleyicinin örtülü dönüşümler için bu kurucuyu kullanmasını engeller. Yukarıdaki sınıfa eklenmesi, işlev çağrısında bir derleyici hatası oluşturacaktır. DoBar (42). Artık açıkça dönüşümle aranmak gerekiyor DoBar (Foo (42))

Bunu yapmak isteyebileceğiniz neden, hataları gizleyebilecek yanlışlıkla yapılan işlemlerden kaçınmaktır. Contrived örnek:

  • Senin bir MyString(int size) Verilen boyutta bir dizi oluşturan bir kurucu ile sınıf. Bir işlevin var print(const MyString&)ve sen ara print(3) (sen ne zaman aslında aramak istendi print("3")). "3" yazmasını beklersiniz, ancak bunun yerine boş bir uzunluk 3 dizesi yazdırır.

2755
2017-09-23 14:09



güzel yazmak, varsayılan parametreler ile çoklu arg ctors de tek arg ctor, örneğin, Object (const char * name = NULL, int otype = 0) olarak davranabilir. - maccullt
Sanırım tek bir argüman kurucularını başlangıçta (daha fazla veya daha az otomatik) açık olarak düşünmeyi ve açık anahtar kelimeyi yalnızca örtük dönüştürme istendiğinde kaldırmayı düşünmeliyiz. tasarım tarafından. Muhakkak çalışmacıların, örtük bir dönüşüm olarak çalışabilmelerini sağlamak için 'örtük' bir anahtar kelime ile varsayılan olarak açık olması gerektiğini düşünüyorum. Ama bu böyle değil. - Michael Burr
@thecoshman: Bir bildirme parametre  explicit - sen ilan et inşaatçı  explicit. Ama evet: tipiniz Foo inşa edilmek zorunda expliciteOnlar, yapıcılarının parametrelerini sadece fonksiyona takarak sessizce inşa edilmeyecekler. - Christian Severin
Sadece bir FYI örneğinizde "print (3)" dediğinizde, fonksiyonun "print (const MyString &") olması gerekir. Burada "const" zorunludur, çünkü 3 geçici bir "MyString" nesnesine dönüştürülür ve "const" olmadığı sürece bir referansa geçici olarak bağlanamazsınız (yine de C ++ gotchas'ın uzun bir listesinde başka bir tane). - Larry
Bütünlük uğruna, ben parametre dönüşüm ek olarak ekliyorum açık Buradaki anahtar kelime aynı zamanda bir kopya ctorunun (örneğin Foo myFoo = 42;) atama formunun kullanımını önleyecek ve Foo myFoo = Foo (42) açık formlarını gerektirecektir; veya Foo myFoo (42); - Arbalest


Varsayalım, bir sınıfınız var. String:

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

Şimdi, eğer denersen:

String mystring = 'x';

Karakter 'x' örtülü olarak dönüştürülecek int ve sonra String(int) kurucu çağrılacak. Ancak, kullanıcının istediği bu değildi. Dolayısıyla, bu tür durumları önlemek için, kurucuyu explicit:

class String {
public:
    explicit String (int n); //allocate n bytes
    String(const char *p); // initialize sobject with string p
};

964
2017-09-23 16:37



Ve C ++ 0x yeni genelleştirilmiş başlatma kurallarının String s = {0}; kötü biçimlendirici ile diğer kurucuyu çağırmaya çalışmaktan ziyade String s = 0; yapardım. - Johannes Schaub - litb
Bu eski bir soru olsa da, bazı şeyleri işaret etmeye değecek gibi görünüyor (ya da birisinin beni düzleştirmesi). Int formu veya her iki ctors, 'açık' yaparak, eğer hala kullanırsanız aynı hataya sahip olursunuz String mystring('x') ne zaman demek istedin String mystring("x") yapmaz mısın Ayrıca, yukarıdaki yorumdan gelişmiş davranışını görüyorum String s = {0} üzerinde String s = 0 ctor 'int açık' int formu yapma sayesinde. Ancak, ctors'in önceliklerini bilmek dışında, bunun amacını nasıl bileceksiniz (yani hatayı nasıl tespit edeceğiniz) String s{0} ? - Arbalest
Neden String mystring = 'x'; int dönüşüyor? - InQusitive
@InQusitive: 'x'bir tamsayı olarak ele alınmaktadır çünkü char veri türü sadece 1 baytlık bir tamsayıdır. - DavidRR
Örneğinizdeki problem, sadece bununla birlikte çalışmasıdır. kopyalamayı başlat (kullanarak =) ama değil doğrudan başlatma (kullanmadan =): derleyici yine de String(int) yazarsanız bir hata oluşturmadan kurucu String mystring('x');, @Arbalest'in işaret ettiği gibi. explicit anahtar kelime, doğrudan başlatma ve işlev çözünürlüğünde gerçekleşen örtülü dönüşümleri engellemek içindir. Örneğinize daha iyi bir çözüm, kurucunun basit bir aşırı yüklenmesi olabilir: String(char c);. - Maggyero


C ++ 'da, yalnızca bir gerekli parametreye sahip bir kurucu, örtük bir dönüşüm işlevi olarak kabul edilir. Parametre tipini sınıf tipine dönüştürür. Bunun iyi bir şey olup olmadığı, kurucunun semantiğine bağlıdır.

Örneğin kurucu ile bir dize sınıfınız varsa String(const char* s)Muhtemelen tam olarak istediğin bu. Sen geçebilirsin const char* beklemekte olan bir işleve Stringve derleyici otomatik olarak geçici olarak String senin için nesne.

Öte yandan, kurucusu olan bir buffer sınıfınız varsa Buffer(int size) arabellek boyutunu bayt cinsinden alır, muhtemelen derleyicinin sessizce dönmesini istemezsiniz intiçine Buffers. Bunu önlemek için kurucuyu explicit anahtar kelime:

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

Bu şekilde,

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

derleme zamanı hatası olur. Eğer geçici geçmek istiyorsan Buffer nesne, bunu açıkça yapmanız gerekir:

useBuffer(Buffer(4));

Özetle, tek parametreli yapıcınız parametreyi sınıfınızın bir nesnesine dönüştürürse, muhtemelen kullanmak istemezsiniz. explicit Anahtar kelime. Ancak, yalnızca tek bir parametre almayı gerektiren bir kurucunuz varsa, bunu explicitDerleyicinin beklenmedik dönüşümlerle sizi şaşırtmasını engellemek için.


130
2017-10-08 14:43



useBuffer argümanı için bir değer bekliyor, useBuffer(Buffer(4)) onun yüzünden de çalışmayacak. Bunu almak için değiştirmek const Buffer& veya Buffer&& ya da sadece Buffer işe yarayacaktı. - pqnet


Bu cevap, diğer cevaplarda ele alınmadığı için açık bir kurucu ile / olmadan nesne oluşturma ile ilgilidir.

Açık bir yapıcı olmadan aşağıdaki sınıfı göz önünde bulundurun:

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

private:
    int m_x;
};

Foo sınıfı nesneleri 2 şekilde oluşturulabilir:

Foo bar1(10);

Foo bar2 = 20;

Uygulamaya bağlı olarak, ikinci sınıftaki Foo sınıfı, kafa karıştırıcı olabilir veya programlayıcının amaçladığı şey olmayabilir. Önek explicit kurucuya anahtar kelime bir derleyici hatası oluşturur Foo bar2 = 20;.

Bu genellikle Tek argüman kurucular olarak bildirmek için iyi bir uygulama explicitUygulamanız özellikle yasaklamıyorsa,

Ayrıca, kurucular

  • tüm parametreler için varsayılan argümanlar veya
  • ikinci parametre için varsayılan argümanlar

her ikisi de tek argüman kurucular olarak kullanılabilir. Yani bunları da yapmak isteyebilirsiniz explicit.

Kasten olacağın bir örnek değil Tek değişkenli kurucunuzu açık yapmak istiyorsanız, bir functor oluşturuyorsanız (bkz. 'add_x' yapısına bakın) bu Cevap). Böyle bir durumda, bir nesneyi add_x add30 = 30; Muhtemelen mantıklı olur.

İşte açık kurucular hakkında iyi bir yazma.


34
2017-11-21 02:36





explicit anahtar kelime, dönüşüm oluşturucuya dönüştürücü yapmaz. Sonuç olarak, kod daha az hata eğilimli.


31
2017-07-10 23:48



Eğer c ++ biliyorsan düz cevap, eğer sen cjm cevabına gidemezsen ... - n611x007


Anahtar kelime explicit eşlik etmek

  • ilk (herhangi bir) parametreyi örtük olarak X'e dönüştürmek için kullanılamayacak bir sınıf X yapıcısı

C ++ [class.conv.ctor]

1) İşlev belirteci açıklanmayan bir kurucu, parametrelerinin türlerinden sınıfının türüne bir dönüşüm belirtir. Böyle bir kurucuya dönüştürme kurucusu denir.

2) Açık bir kurucu, açık olmayan yapıcılar gibi nesneler oluşturur, ancak bunu yalnızca doğrudan başlatma sözdizimiyle (8.5) veya yayınların (5.2.9, 5.4) açıkça kullanıldığı yerlerde yapar. Bir varsayılan kurucu açık bir kurucu olabilir; Böyle bir kurucu, varsayılan başlatma veya değer sıfırlamayı gerçekleştirmek için kullanılacaktır   (8.5).

  • veya sadece doğrudan başlatma ve açık dönüşüm için dikkate alınan bir dönüşüm işlevi.

C ++ [class.conv.fct]

2) Bir dönüşüm işlevi açık (7.1.2) olabilir, bu durumda yalnızca doğrudan başlatma için kullanıcı tanımlı bir dönüşüm olarak kabul edilir (8.5). Aksi takdirde, kullanıcı tanımlı dönüşümler ödevlerde kullanılmaya uygun değildir   ve başlatmalar.

genel bakış

Açık dönüşüm işlevleri ve kurucular yalnızca açık dönüşümler (doğrudan başlatma veya açık yayınlama işlemi) için kullanılabilirken, açık olmayan yapıcılar ve dönüşüm işlevleri örtük ve açık dönüşümler için kullanılabilir.

/*
                                 explicit conversion          implicit conversion

 explicit constructor                    yes                          no

 constructor                             yes                          yes

 explicit conversion function            yes                          no

 conversion function                     yes                          yes

*/

Yapıları kullanarak örnek X, Y, Z ve fonksiyonlar foo, bar, baz:

Aradaki farkı görmek için küçük bir yapıya ve fonksiyonlara bakalım. explicitve olmayanexplicit dönüşümleri.

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) { }

Kurucu ile ilgili örnekler:

Bir fonksiyon argümanının dönüştürülmesi:

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

Nesne başlatma:

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

Dönüşüm işlevleriyle ilgili örnekler:

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

Bir fonksiyon argümanının dönüştürülmesi:

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

Nesne başlatma:

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

Neden kullanmak explicit dönüşüm fonksiyonları veya kurucular?

Dönüşüm yapıcılar ve açık olmayan dönüşüm işlevleri belirsizliği ortaya çıkarabilir.

Bir yapı düşünün V, üstü açık int, yapı U dolaylı olarak yapılandırılabilir V ve bir işlev f için aşırı yüklü U ve bool sırasıyla.

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

struct U { U(V) { } };

void f(U) { }
void f(bool) {  }

Bir çağrı f bir nesne türünü geçiriyorsa belirsizdir V.

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

Derleyici, kurucuyu kullanıp kullanmayacağını bilmiyor U veya dönüştürmek için dönüştürme işlevi V nesneye geçmek için bir nesneye f.

Ya kurucunun U veya dönüşüm işlevi V olabilir explicitSadece açık olmayan dönüşüm dikkate alınacağı için hiçbir belirsizlik olmayacaktır. Her ikisi de açıksa, f bir nesne kullanarak V Açık bir dönüşüm veya döküm işlemi kullanılarak yapılmalıdır.

Dönüşüm yapıcıları ve açık olmayan dönüşüm işlevleri beklenmedik davranışlara yol açabilir.

Bazı vektörlerin yazdırıldığı bir işlevi düşünün:

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

Vektörün boyut kurucusu açık olmazsa, bu işlevi çağırmak mümkün olabilirdi:

print_intvector(3);

Böyle bir çağrıdan ne beklenebilir? İçeren bir satır 3 veya üç satır içeren 0? (İkincisi, ne olduğu.)

Bir sınıf arabirimindeki açık anahtar sözcüğün kullanılması, arabirimin kullanıcı tarafından istenen bir dönüşüm hakkında açık olması için zorlar.

Bjarne Stroustrup'un ("C ++ Programlama Dili", 4. Baskı, 35.2.1, s. 1011) nedenini sorduğu gibi std::duration düz bir sayıdan örtülü olarak yapılamaz:

Ne demek istediğini biliyorsan, bunun hakkında açık ol.


31
2018-05-14 09:28





explicit-keyword çağrılacak bir kurucu zorlamak için kullanılabilir açıkça.

class C{
public:
    explicit C(void) = default;
};

int main(void){
    C c();
    return 0;
}

explicit-keyword yapıcının önünde C(void) Derleyiciye, bu kurucuya yalnızca açık çağrının izin verildiğini söyler.

explicit-keyword, kullanıcı tanımlı tip cast operatörlerinde de kullanılabilir:

class C{
public:
    explicit inline operator bool(void) const{
        return true;
    }
};

int main(void){
    C c;
    bool b = static_cast<bool>(c);
    return 0;
}

İşte, explicit-keyword, yalnızca geçerli yayınların geçerli olmasını zorunlu kılar. bool b = c; Bu durumda geçersiz bir döküm olur. Böyle durumlarda explicit-keyword, programcıya örtülü, istenmeyen dökümlerden kaçınmaya yardımcı olabilir. Bu kullanım standartlaştırılmıştır. C ++ 11.


25
2017-10-01 22:00



C c(); İlk örnekte bunun ne anlama geldiğini kastettiğiniz anlamına gelmez: bu bir adlı isminin bildirilmesidir c hiçbir parametre almaz ve bir örnek döndürür C. - 6502
explicit operator bool() ayrıca güvenli bool C ++ 11 sürümüdür ve durum kontrollerinde örtülü olarak kullanılabilir (ve bir tek durum kontrollerinde, bildiğim kadarıyla). İkinci örneğinizde, bu satır aynı zamanda geçerli olacaktır. main(): if (c) { std::cout << "'c' is valid." << std:: endl; }. Bununla birlikte, açık bir döküm olmadan da kullanılamaz. - Justin Time
"kurucu açıkça çağrılacak" yok hayır - curiousguy
@JustinTime Bu güvenli bool bir inane, kırık bir versiyonu. Açık örtük dönüşümün bütün fikri saçmadır. - curiousguy
@curiousguy Doğru. Biraz daha kolay anlaşılmayı hedefleyen bir kükdeye benziyor (bu, muhtemelen İngilizce'ye mantıkla çevirmenin ümidindeyse) ve önceki güvenli bool uygulamalarıyla tamamen uyumlu olmamak için tasarlandı. takas ederseniz bir şeyleri kırma olasılığı vardır. IMO, en azından. - Justin Time


Bu zaten tartışıldı (açık kurucu nedir). Ama şunu söylemeliyim ki, burada bulunan detaylı açıklamalardan yoksundur.

Ayrıca, daha önce belirtildiği gibi, bir argüman kurucularınızı (arg2, arg3, ... için varsayılan değerleri olanlar dahil) yapmak her zaman iyi bir kodlama uygulamasıdır. C ++ ile her zaman olduğu gibi: eğer yapmazsan - yapmanı isterdin ...

Sınıflar için bir başka iyi uygulama, gerçekten uygulamak gerekmedikçe, kopya oluşturma ve atamayı özel yapmak (a.k.a. Bu, varsayılan olarak sizin için C ++ 'nin oluşturacağı yöntemleri kullanırken, işaretçilerin nihai kopyalarına sahip olmayı önler. Bunu yapmanın başka bir yolu, desteklenemez.


17
2017-08-20 12:45



Bu yazı 2009 yılında yazılmıştır. Bugün bunları özel olarak değil, daha doğrusu = delete. - v010dya
@ linki bozuksa, soru silindi - David Cabrera


Cpp Referans her zaman yararlıdır! Açık belirteci hakkında ayrıntılar bulunabilir İşte. Bakmak gerekebilir örtülü dönüşümler ve kopyalamaya karşı başlatma çok.

Hızlı bakış

Açık belirteci, bir kurucu veya dönüştürme işlevinin (C ++ 11'den beri) örtülü dönüşümlere veya kopya başlatma durumuna izin vermediğini belirtir.

Aşağıdaki gibi örnek:

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
2017-12-20 12:19



Evet! Örnek çalışmanın ve cppreference'da anahtar kelimenin açıklamasının incelenmesi, anlamanın en iyi yoludur. explicit Bence. - dekuShrub
explicit operator bool() vs. if özel bir durumdur. Tanımlanmış kullanıcı ile çoğaltmanın hiçbir yolu yoktur. Bool, explicit operator Bool() ve bir işlev çağırdı If. - curiousguy


Açık dönüşüm yapıcılar (yalnızca C ++)

Açık işlev belirteci, istenmeyen örtülü türü kontrol eder   dönüşümleri. Sadece kurucu beyanlarında kullanılabilir   bir sınıf beyanı içinde. Örneğin, varsayılan dışında   kurucu, aşağıdaki sınıftaki kurucular dönüşüm   kurucular.

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

Aşağıdaki beyanlar yasaldır:

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

İlk beyannameye eşdeğerdir A c = A( 1 );.

Sınıfın kurucusunu ilan ederseniz explicitÖnceki beyanlar yasadışı olurdu.

Örneğin, sınıfı şu şekilde bildirirseniz:

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

Yalnızca sınıf türünün değerleriyle eşleşen değerleri atayabilirsiniz.

Örneğin, aşağıdaki ifadeler yasaldır:

  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);

9
2018-01-23 09:26