Arti = hapus setelah deklarasi fungsi

242
class my_class
{
    ...
    my_class(my_class const &) = delete;
    ...
};

Apa = deleteartinya dalam konteks itu?

Apakah ada "pengubah" lain (selain = 0dan = delete)?

Pat O'Keefe
sumber
23
@ Blindy Ini akan menjadi standar dalam C ++ 0x, yaitu segera.
Konrad Rudolph
1
Saya berdiri dengan benar, saya telah melewatkan fitur C ++ 0x ini. Saya berpikir itu adalah a #definela Qt yang dievaluasi menjadi 0 dan kemudian menyatakan fungsi tersembunyi atau sesuatu.
Blindy
Saya telah mengingat kata kunci 'nonaktifkan' yang artinya sama atau mirip. Apakah saya membayangkannya? Atau adakah perbedaan tipis di antara mereka?
Stewart

Jawaban:

201

Menghapus fungsi adalah fitur C ++ 11 :

Ungkapan umum "melarang penyalinan" sekarang dapat diungkapkan secara langsung:

class X {
    // ...
    X& operator=(const X&) = delete;  // Disallow copying
    X(const X&) = delete;
};

[...]

Mekanisme "hapus" dapat digunakan untuk fungsi apa pun. Misalnya, kami dapat menghilangkan konversi yang tidak diinginkan seperti ini:

struct Z {
    // ...

    Z(long long);     // can initialize with an long long         
    Z(long) = delete; // but not anything less
};
Prasoon Saurav
sumber
3
Bukankah metode tradisional untuk "melarang penyalinan" hanya untuk membuat copy-ctor dan operator = "pribadi?" Ini sedikit lebih jauh dan menginstruksikan kompiler untuk tidak menghasilkan fungsi. Jika keduanya bersifat pribadi dan = hapus, apakah menyalin dua kali dilarang?
Reb.Cabin
8
@Reb, =deletemembuat metode tidak dapat diakses bahkan dari konteks yang dapat melihat privatemetode (yaitu di dalam kelas dan teman-temannya). Ini menghilangkan ketidakpastian ketika Anda membaca kode. @Prasoon, contoh kedua itu masih hanya menghapus konstruktor - alangkah baiknya melihat yang dihapus operator long ()misalnya.
Toby Speight
2
@B Reb.Cabin Menggunakan = deletelebih baik daripada menggunakan privateatau mekanisme serupa lainnya karena biasanya Anda ingin fungsi terlarang dideklarasikan secara jelas dan dipertimbangkan untuk resolusi overload dll, sehingga dapat gagal sedini mungkin dan memberikan kesalahan paling jelas kepada pengguna. Solusi apa pun yang melibatkan "menyembunyikan" deklarasi mengurangi efek ini.
Leushenko
1
Apakah ada alasan khusus untuk membuat pembuat salinan publik dan menerapkan kata kunci delete. Mengapa tidak membiarkan konstruktor pribadi dan menerapkan kata kunci?
Dohn Joe
81
  1. = 0berarti suatu fungsi adalah virtual murni dan Anda tidak dapat membuat instance objek dari kelas ini. Anda perlu mengambil darinya dan menerapkan metode ini
  2. = deleteberarti kompiler tidak akan menghasilkan konstruktor untuk Anda. AFAIK ini hanya diperbolehkan pada copy constructor dan operator penugasan. Tapi saya tidak terlalu bagus dalam standar yang akan datang.
mkaes
sumber
4
Ada beberapa kegunaan lain dari =deletesintaks. Misalnya, Anda dapat menggunakannya untuk secara eksplisit melarang beberapa jenis konversi implisit yang mungkin terjadi dengan panggilan. Untuk ini, Anda cukup menghapus fungsi kelebihan beban. Lihat halaman Wikipedia di C ++ 0x untuk info lebih lanjut.
LiKao
Saya akan melakukannya segera setelah saya menemukan beberapa. Rasanya sudah waktunya untuk mengejar ketinggalan dengan c ++ 0X
mkaes
Ya, C ++ 0x batu. Saya tidak sabar menunggu GCC 4.5+ menjadi lebih umum, sehingga saya dapat mulai menggunakan lambdas.
LiKao
5
Deskripsi untuk = deletetidak sepenuhnya benar. = deletedapat digunakan untuk fungsi apa pun, dalam hal ini secara eksplisit ditandai sebagai dihapus dan setiap penggunaan menghasilkan kesalahan kompilator. Untuk fungsi anggota khusus, ini juga berarti secara khusus bahwa mereka tidak dihasilkan untuk Anda oleh kompiler, tetapi itu hanya hasil dari penghapusan, dan bukan apa yang = deletesebenarnya.
MicroVirus
28

Kutipan dari Bahasa Pemrograman C ++ ini [Edisi ke-4] - Buku Bjarne Stroustrup berbicara tentang tujuan sebenarnya di balik penggunaan =delete:

3.3.4 Operasi Penekan

Menggunakan salinan default atau memindahkan kelas dalam hierarki biasanya merupakan bencana : hanya diberi pointer ke basis, kita tidak tahu apa yang dimiliki anggota kelas turunan, jadi kita tidak bisa tahu cara menyalinnya . Jadi, hal terbaik yang harus dilakukan biasanya menghapus salinan default dan memindahkan operasi, yaitu untuk menghilangkan definisi default dari kedua operasi tersebut:

class Shape {
public:
  Shape(const Shape&) =delete; // no copy operations
  Shape& operator=(const Shape&) =delete;

  Shape(Shape&&) =delete; // no move operations
  Shape& operator=(Shape&&) =delete;
  ˜Shape();
    // ...
};

Sekarang upaya untuk menyalin Bentuk akan ditangkap oleh kompiler.

The =deleteMekanisme adalah umum, yaitu, dapat digunakan untuk menekan operasi apapun

Saurav Sahu
sumber
5

Standar pengkodean yang telah saya kerjakan memiliki yang berikut untuk sebagian besar deklarasi kelas.

//  coding standard: disallow when not used
T(void)                  = delete; // default ctor    (1)
~T(void)                 = delete; // default dtor    (2)
T(const T&)              = delete; // copy ctor       (3)
T(const T&&)             = delete; // move ctor       (4)
T& operator= (const T&)  = delete; // copy assignment (5)
T& operator= (const T&&) = delete; // move assignment (6)

Jika Anda menggunakan salah satu dari 6 ini, Anda cukup mengomentari baris yang sesuai.

Contoh: kelas FizzBus hanya membutuhkan dtor, dan karenanya tidak menggunakan 5 lainnya.

//  coding standard: disallow when not used
FizzBuzz(void)                         = delete; // default ctor (1)
// ~FizzBuzz(void);                              // dtor         (2)
FizzBuzz(const FizzBuzz&)              = delete; // copy ctor    (3)
FizzBuzz& operator= (const FizzBuzz&)  = delete; // copy assig   (4)
FizzBuzz(const FizzBuzz&&)             = delete; // move ctor    (5)
FizzBuzz& operator= (const FizzBuzz&&) = delete; // move assign  (6)

Kami berkomentar hanya 1 di sini, dan menginstal pelaksanaannya di tempat lain (mungkin di mana standar pengkodean menyarankan). 5 lainnya (dari 6) tidak diizinkan menghapus.

Anda juga dapat menggunakan '= hapus' untuk melarang promosi tersirat dari berbagai nilai ukuran ... contoh

// disallow implicit promotions 
template <class T> operator T(void)              = delete;
template <class T> Vuint64& operator=  (const T) = delete;
template <class T> Vuint64& operator|= (const T) = delete;
template <class T> Vuint64& operator&= (const T) = delete;
2785528
sumber
3

= deleteadalah fitur yang diperkenalkan di C ++ 11. Sesuai =deleteitu tidak akan diizinkan untuk memanggil fungsi itu.

Secara terperinci.

Misalkan di kelas.

Class ABC{
 Int d;
 Public:
  ABC& operator= (const ABC& obj) =delete
  {

  }
};

Saat memanggil fungsi ini untuk penugasan keberatan, itu tidak akan diizinkan. Berarti operator penugasan akan membatasi untuk menyalin dari satu objek ke objek lain.

ashutosh
sumber
2

Standar C ++ 0x baru. Silakan lihat bagian 8.4.3 dalam konsep kerja N3242

dubnde
sumber
Wah, draft itu sudah ketinggalan zaman. Berikut ini adalah yang terbaru (per 3 April 2011): open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
TonyK
Terima kasih dan perbarui tautannya. Sangat membantu untuk mendapatkan konsep saat ini. Bagian / konten yang dirujuk sudah benar bahkan di draft lama jadi saya tidak mengerti suara turun.
dubnde
1

Fungsi yang dihapus secara implisit sebaris

(Tambahan untuk jawaban yang ada)

... Dan fungsi yang dihapus akan menjadi deklarasi pertama dari fungsi (kecuali untuk menghapus spesialisasi eksplisit dari templat fungsi - penghapusan harus pada deklarasi pertama spesialisasi), yang berarti Anda tidak dapat mendeklarasikan fungsi dan kemudian menghapusnya, katakanlah, pada definisi lokal ke unit terjemahan.

Mengutip [dcl.fct.def.delete] / 4 :

Fungsi yang dihapus secara implisit sebaris. ( Catatan: Aturan satu-definisi ( [basic.def.odr] ) berlaku untuk definisi yang dihapus. - Catatan akhir ] Definisi yang dihapus dari suatu fungsi harus merupakan deklarasi fungsi pertama atau, untuk spesialisasi eksplisit dari templat fungsi. , deklarasi pertama spesialisasi itu. [Contoh:

struct sometype {
  sometype();
};
sometype::sometype() = delete;      // ill-formed; not first declaration

- contoh akhir )

Templat fungsi utama dengan definisi yang dihapus dapat dikhususkan

Meskipun aturan umum adalah untuk menghindari templat fungsi khusus karena spesialisasi tidak berpartisipasi dalam langkah pertama resolusi kelebihan beban, ada beberapa konteks yang dapat diperdebatkan. Misalnya ketika menggunakan templat fungsi utama yang tidak kelebihan beban tanpa definisi untuk mencocokkan semua jenis yang tidak ingin orang dikonversi secara implisit ke kelebihan beban yang sesuai dengan konversi; yaitu, untuk secara implisit menghapus sejumlah kecocokan konversi-konversi dengan hanya menerapkan kecocokan jenis yang tepat dalam spesialisasi eksplisit dari templat fungsi primer yang tidak didefinisikan, tidak kelebihan beban.

Sebelum konsep fungsi C ++ 11 yang dihapus, orang bisa melakukan ini hanya dengan menghilangkan definisi templat fungsi utama, tetapi ini memberikan kesalahan referensi yang tidak terdefinisi yang tidak jelas yang tidak memberikan maksud semantik sama sekali dari pembuat templat fungsi primer (sengaja dihilangkan) ?). Jika kita sebaliknya secara eksplisit menghapus templat fungsi utama, pesan kesalahan jika tidak ditemukan spesialisasi eksplisit yang cocok menjadi jauh lebih baik, dan juga menunjukkan bahwa penghilangan / penghapusan definisi templat fungsi primer disengaja.

#include <iostream>
#include <string>

template< typename T >
void use_only_explicit_specializations(T t);

template<>
void use_only_explicit_specializations<int>(int t) {
    std::cout << "int: " << t;
}

int main()
{
    const int num = 42;
    const std::string str = "foo";
    use_only_explicit_specializations(num);  // int: 42
    //use_only_explicit_specializations(str); // undefined reference to `void use_only_explicit_specializations< ...
}

Namun, alih-alih hanya menghilangkan definisi untuk templat fungsi utama di atas, menghasilkan kesalahan referensi yang tidak terdefinisi yang tidak jelas ketika tidak ada spesialisasi eksplisit yang cocok, definisi templat primer dapat dihapus:

#include <iostream>
#include <string>

template< typename T >
void use_only_explicit_specializations(T t) = delete;

template<>
void use_only_explicit_specializations<int>(int t) {
    std::cout << "int: " << t;
}

int main()
{
    const int num = 42;
    const std::string str = "foo";
    use_only_explicit_specializations(num);  // int: 42
    use_only_explicit_specializations(str);
    /* error: call to deleted function 'use_only_explicit_specializations' 
       note: candidate function [with T = std::__1::basic_string<char>] has 
       been explicitly deleted
       void use_only_explicit_specializations(T t) = delete; */
}

Menghasilkan pesan kesalahan yang lebih mudah dibaca, di mana niat penghapusan juga terlihat jelas (di mana kesalahan referensi yang tidak terdefinisi dapat menyebabkan pengembang menganggap ini kesalahan yang tidak terpikirkan).

Kembali ke mengapa kita ingin menggunakan teknik ini? Sekali lagi, spesialisasi eksplisit dapat berguna untuk secara implisit menghapus konversi implisit.

#include <cstdint>
#include <iostream>

void warning_at_best(int8_t num) { 
    std::cout << "I better use -Werror and -pedantic... " << +num << "\n";
}

template< typename T >
void only_for_signed(T t) = delete;

template<>
void only_for_signed<int8_t>(int8_t t) {
    std::cout << "UB safe! 1 byte, " << +t << "\n";
}

template<>
void only_for_signed<int16_t>(int16_t t) {
    std::cout << "UB safe! 2 bytes, " << +t << "\n";
}

int main()
{
    const int8_t a = 42;
    const uint8_t b = 255U;
    const int16_t c = 255;
    const float d = 200.F;

    warning_at_best(a); // 42
    warning_at_best(b); // implementation-defined behaviour, no diagnostic required
    warning_at_best(c); // narrowing, -Wconstant-conversion warning
    warning_at_best(d); // undefined behaviour!

    only_for_signed(a);
    only_for_signed(c);

    //only_for_signed(b);  
    /* error: call to deleted function 'only_for_signed' 
       note: candidate function [with T = unsigned char] 
             has been explicitly deleted
       void only_for_signed(T t) = delete; */

    //only_for_signed(d);
    /* error: call to deleted function 'only_for_signed' 
       note: candidate function [with T = float] 
             has been explicitly deleted
       void only_for_signed(T t) = delete; */
}
dfri
sumber
0

Ini adalah hal baru dalam standar C ++ 0x di mana Anda dapat menghapus fungsi yang diwarisi.

Tayyab
sumber
11
Anda dapat menghapus fungsi apa pun. Misalnya, void foo(int); template <class T> void foo(T) = delete;menghentikan semua konversi implisit. Hanya argumen inttipe yang diterima, semua yang lain akan mencoba untuk instantiate fungsi "dihapus".
UncleBens