Pertanyaan Apa arti kata kunci eksplisit?


Apa yang dimaksud dengan explicit kata kunci berarti di C + +?


2363
2017-09-23 13:58


asal


Jawaban:


Compiler diperbolehkan membuat satu konversi implisit untuk menyelesaikan parameter ke fungsi. Apa artinya ini adalah bahwa kompiler dapat menggunakan konstruktor dapat dipanggil dengan parameter tunggal untuk mengkonversi dari satu jenis ke jenis lainnya untuk mendapatkan jenis yang tepat untuk parameter.

Berikut ini contoh kelas dengan konstruktor yang dapat digunakan untuk konversi implisit:

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

Inilah fungsi sederhana yang membutuhkan Foo obyek:

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

dan di sinilah tempatnya DoBar fungsi disebut.

int main ()
{
  DoBar (42);
}

Argumennya bukan a Foo objek, tetapi sebuah int. Namun, ada konstruktor untuk Foo yang membutuhkan int jadi konstruktor ini dapat digunakan untuk mengubah parameter ke tipe yang benar.

Compiler diperbolehkan melakukan ini sekali untuk setiap parameter.

Awalan explicit kata kunci ke konstruktor mencegah compiler dari menggunakan konstruktor untuk konversi implisit. Menambahkannya ke kelas di atas akan membuat kesalahan kompiler di panggilan fungsi DoBar (42). Sekarang perlu untuk memanggil konversi secara eksplisit dengan DoBar (Foo (42))

Alasan Anda mungkin ingin melakukan ini adalah untuk menghindari konstruksi yang disengaja yang dapat menyembunyikan bug. Contoh yang dibuat:

  • Anda memiliki MyString(int size) kelas dengan konstruktor yang membangun string ukuran yang diberikan. Anda memiliki fungsi print(const MyString&), dan Anda menelepon print(3) (ketika kamu sebenarnya dimaksudkan untuk menelepon print("3")). Anda berharap untuk mencetak "3", tetapi mencetak string kosong dengan panjang 3 sebagai gantinya.

2749
2017-09-23 14:09



Misalkan, Anda memiliki kelas String:

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

Sekarang, jika Anda mencoba:

String mystring = 'x';

Karakter 'x' akan dikonversi secara implisit menjadi int dan kemudian String(int) konstruktor akan dipanggil. Tapi, ini bukan apa yang dimaksudkan pengguna. Jadi, untuk mencegah kondisi seperti itu, kita akan mendefinisikan konstruktor sebagai explicit:

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

962
2017-09-23 16:37



Dalam C ++, sebuah konstruktor dengan hanya satu parameter yang diperlukan dianggap sebagai fungsi konversi implisit. Ini mengubah tipe parameter ke tipe kelas. Apakah ini adalah hal yang baik atau tidak tergantung pada semantik konstruktor.

Sebagai contoh, jika Anda memiliki kelas string dengan konstruktor String(const char* s), mungkin itulah yang Anda inginkan. Anda bisa lewat a const char* ke fungsi yang mengharapkan String, dan compiler secara otomatis akan membangun sementara String keberatan untuk Anda.

Di sisi lain, jika Anda memiliki kelas penyangga yang konstruktor Buffer(int size) mengambil ukuran buffer dalam byte, Anda mungkin tidak ingin kompilator diam-diam berubah ints ke Buffers. Untuk mencegah hal itu, Anda menyatakan konstruktor dengan explicit kata kunci:

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

Dengan begitu,

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

menjadi kesalahan waktu kompilasi. Jika Anda ingin lulus sementara Buffer objek, Anda harus melakukannya secara eksplisit:

useBuffer(Buffer(4));

Singkatnya, jika konstruktor parameter tunggal Anda mengubah parameter menjadi objek kelas Anda, Anda mungkin tidak ingin menggunakan explicit kata kunci. Tetapi jika Anda memiliki konstruktor yang kebetulan mengambil satu parameter, Anda harus menyatakannya sebagai explicituntuk mencegah kompilator mengherankan Anda dengan konversi yang tidak terduga.


130
2017-10-08 14:43



Jawaban ini adalah tentang pembuatan objek dengan / tanpa konstruktor eksplisit karena tidak tercakup dalam jawaban lainnya.

Pertimbangkan kelas berikut tanpa konstruktor eksplisit:

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

private:
    int m_x;
};

Objek kelas Foo dapat dibuat dalam 2 cara:

Foo bar1(10);

Foo bar2 = 20;

Tergantung pada implementasi, cara kedua dari instantiating class Foo mungkin membingungkan, atau tidak seperti yang diinginkan programmer. Awalan explicit kata kunci ke konstruktor akan menghasilkan kesalahan kompiler di Foo bar2 = 20;.

ini biasanya praktik yang baik untuk menyatakan konstruk argumen tunggal sebagai explicit, kecuali penerapan Anda secara khusus melarangnya.

Perhatikan juga bahwa konstruktor dengan

  • argumen default untuk semua parameter, atau
  • argumen default untuk parameter kedua dan seterusnya

keduanya dapat digunakan sebagai konstruktor satu argumen. Jadi Anda mungkin ingin membuatnya juga explicit.

Contoh ketika Anda akan sengaja tidak ingin membuat konstruktor argumen tunggal Anda eksplisit adalah jika Anda membuat functor (lihat pada struct 'add_x' yang dideklarasikan ini menjawab). Dalam kasus seperti itu, membuat objek sebagai add_x add30 = 30; mungkin masuk akal.

Sini adalah tulisan yang bagus tentang konstruktor eksplisit.


34
2017-11-21 02:36



Itu explicit kata kunci membuat konstruktor konversi menjadi konstruktor non-konversi. Akibatnya, kode ini kurang rentan kesalahan.


31
2017-07-10 23:48



Kata kunci explicit menyertai baik

  • konstruktor kelas X yang tidak dapat digunakan secara implisit mengubah parameter pertama (hanya) ke tipe X

C ++ [class.conv.ctor]

1) Konstruktor yang dinyatakan tanpa specifier fungsi eksplisit menentukan konversi dari jenis parameternya ke jenis kelasnya. Konstruktor seperti ini disebut konstruktor konversi.

2) Sebuah konstruktor eksplisit membangun objek seperti konstruktor non-eksplisit, tetapi hanya melakukannya di mana sintaksis inisialisasi langsung (8.5) atau di mana gips (5.2.9, 5.4) secara eksplisit digunakan. Konstruktor default dapat berupa konstruktor eksplisit; konstruktor seperti itu akan digunakan untuk melakukan inisialisasi default atau inisialisasi nilai   (8,5).

  • atau fungsi konversi yang hanya dipertimbangkan untuk inisialisasi langsung dan konversi eksplisit.

C ++ [class.conv.fct]

2) Fungsi konversi mungkin eksplisit (7.1.2), dalam hal ini hanya dianggap sebagai konversi yang ditentukan pengguna untuk inisialisasi langsung (8.5). Jika tidak, konversi yang ditentukan pengguna tidak dibatasi untuk digunakan dalam tugas   dan inisialisasi.

Ikhtisar

Fungsi konversi eksplisit dan konstruktor hanya dapat digunakan untuk konversi eksplisit (inisialisasi langsung atau operasi cor eksplisit) sementara konstruktor non-eksplisit dan fungsi konversi dapat digunakan untuk implisit serta konversi eksplisit.

/*
                                 explicit conversion          implicit conversion

 explicit constructor                    yes                          no

 constructor                             yes                          yes

 explicit conversion function            yes                          no

 conversion function                     yes                          yes

*/

Contoh menggunakan struktur X, Y, Z dan fungsi foo, bar, baz:

Mari kita lihat pengaturan struktur dan fungsi kecil untuk melihat perbedaan antara explicitdan tidakexplicit konversi.

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

Contoh tentang konstruktor:

Konversi dari argumen fungsi:

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

Inisialisasi objek:

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

Contoh tentang fungsi konversi:

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

Konversi dari argumen fungsi:

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

Inisialisasi objek:

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

Mengapa digunakan? explicit fungsi konversi atau konstruktor?

Konstruktor konversi dan fungsi konversi non-eksplisit dapat memperkenalkan ambiguitas.

Pertimbangkan sebuah struktur V, dapat dikonversi menjadi int, sebuah struktur U implisit konstruktif dari V dan sebuah fungsi f kelebihan beban untuk U dan bool masing-masing.

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

struct U { U(V) { } };

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

Panggilan ke f ambigu jika melewati objek bertipe V.

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

Compiler tidak tahu apakah akan menggunakan konstruktor U atau fungsi konversi untuk mengonversi V objek menjadi tipe untuk diteruskan f.

Jika salah satu konstruktor U atau fungsi konversi V akan menjadi explicit, tidak akan ada ambiguitas karena hanya konversi non-eksplisit yang akan dipertimbangkan. Jika keduanya adalah panggilan eksplisit untuk f menggunakan objek tipe V harus dilakukan menggunakan konversi eksplisit atau operasi cor.

Konstruktor konversi dan fungsi konversi non-eksplisit dapat menyebabkan perilaku tak terduga.

Pertimbangkan fungsi mencetak beberapa vektor:

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

Jika ukuran-konstruktor dari vektor tidak akan eksplisit itu akan mungkin untuk memanggil fungsi seperti ini:

print_intvector(3);

Apa yang diharapkan dari panggilan seperti itu? Satu baris berisi 3 atau tiga baris yang mengandung 0? (Di mana yang kedua adalah apa yang terjadi.)

Menggunakan kata kunci eksplisit dalam antarmuka kelas memaksa pengguna antarmuka untuk secara eksplisit tentang konversi yang diinginkan.

Seperti Bjarne Stroustrup katakan (dalam "Bahasa Pemrograman C ++", 4th Ed., 35.2.1, pp. 1011) pada pertanyaan mengapa std::duration tidak dapat secara implisit dibangun dari angka biasa:

Jika Anda tahu apa yang Anda maksudkan, jelaskan hal itu.


31
2018-05-14 09:28



Itu explicit-kata kunci dapat digunakan untuk menegakkan konstruktor yang akan dipanggil secara eksplisit.

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

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

itu explicit-kata kunci di depan konstruktor C(void) memberitahu kompilator bahwa hanya panggilan eksplisit ke konstruktor ini yang diizinkan.

Itu explicit-kata kunci juga dapat digunakan dalam operator cor jenis yang ditentukan pengguna:

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

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

Sini, explicit-kata kunci hanya memaksakan gips eksplisit agar valid, jadi bool b = c; akan menjadi pemain yang tidak valid dalam kasus ini. Dalam situasi seperti ini explicit-kata kunci dapat membantu programmer untuk menghindari implisit, gips yang tidak diinginkan. Penggunaan ini telah distandarisasi dalam C ++ 11.


25
2017-10-01 22:00



Ini sudah dibahas (apa itu konstruktor eksplisit). Tetapi saya harus mengatakan, bahwa itu tidak memiliki deskripsi rinci yang ditemukan di sini.

Selain itu, selalu merupakan praktik pengkodean yang baik untuk membuat konstruktor satu argumen Anda (termasuk yang dengan nilai default untuk arg2, arg3, ...) sebagaimana telah dinyatakan. Seperti biasa dengan C ++: jika Anda tidak - Anda akan berharap ...

Praktik lain yang bagus untuk kelas adalah membuat konstruksi salinan dan penugasan pribadi (a.k.a. disable it) kecuali Anda benar-benar perlu menerapkannya. Ini menghindari memiliki salinan pointer saat menggunakan metode yang C ++ akan buat untuk Anda secara default. Cara lain untuk melakukan ini adalah berasal dari boost :: noncopyable.


17
2017-08-20 12:45