Anlamı: İşlev bildiriminden sonra sil


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

= deleteBu bağlamda ne anlama geliyor?

Başka "değiştiriciler" ( = 0ve dışında = delete) var mı?


23
@Blindy C ++ 0x'de standart olacak, yani yakında.
Konrad Rudolph

1
Ben düzeltildi standı, bu C ++ 0x özelliğini kaçırmıştı. Ben #define0 olarak değerlendirilen ve daha sonra gizli bir işlev ya da bir şey ilan a la Qt olduğunu düşünüyordum.
Blindy

Aynı veya benzer bir şey anlamına gelen 'devre dışı bırak' anahtar kelimesini hatırlıyorum. Hayal ediyor muyum? Yoksa aralarında ince bir fark var mı?
Stewart

Yanıtlar:


201

Bir işlevi silmek bir C ++ 11 özelliğidir :

Ortak "kopyalamayı yasaklama" deyimi doğrudan ifade edilebilir:

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

[...]

"Sil" mekanizması herhangi bir işlev için kullanılabilir. Örneğin, istenmeyen bir dönüşümü şu şekilde ortadan kaldırabiliriz:

struct Z {
    // ...

    Z(long long);     // can initialize with an long long         
    Z(long) = delete; // but not anything less
};

3
Yalnızca copy-ctor ve operator = "private" yapmak için "kopyalamayı yasaklamak" için geleneksel yöntem değil midir? Bu biraz daha ileri gider ve derleyiciye fonksiyonları bile üretmemesini söyler. Hem gizli hem de = sil, kopyalamak iki katına yasak mı?
Reb.Cabin

8
@Reb, yöntemleri =deletegörebilen bağlamlardan bile private(örneğin sınıf ve arkadaşları içinde) yönteme erişilemez hale getirir . Bu, kodu okurken belirsizlikleri ortadan kaldırır. @Prasoon, bu ikinci örnek hala sadece kurucuları siler - operator long ()örneğin silinenleri görmek güzel olurdu .
Toby Speight

2
@ Reb.Cabin kullanma = deletedaha iyi kullanarak daha privategenellikle çünkü ya da diğer benzer mekanizmalar istiyorum yasak fonksiyon gözle görülür ilan ve aşırı yük çözünürlük vb için dikkat edilmesi gereken o mümkün olduğunca erken başarısız ve kullanıcıya en net hata sağlayabilir, böylece. Beyanı "gizlemek" i içeren herhangi bir çözüm bu etkiyi azaltır.
Leushenko

1
Kopya oluşturucuyu herkese açık hale getirmek ve delete anahtar sözcüğünü uygulamak için özel bir neden var mı? Neden kurucuyu özel bırakıp anahtar kelimeyi uygulamıyorsunuz?
Dohn Joe

81
  1. = 0bir fonksiyonun saf sanal olduğu ve bu sınıftan bir nesne başlatamadığınız anlamına gelir. Ondan türetmeniz ve bu yöntemi uygulamanız gerekiyor
  2. = deletederleyicinin bu yapıcıları sizin için oluşturmayacağı anlamına gelir. AFAIK, buna yalnızca kopya oluşturucu ve atama işlecinde izin verilir. Ama yaklaşan standartta çok iyi değilim.

4
=deleteSözdiziminin başka kullanımları da vardır . Örneğin, aramada gerçekleşebilecek bir tür örtük dönüşüme açıkça izin vermemek için kullanabilirsiniz. Bunun için aşırı yüklenmiş fonksiyonları silmeniz yeterlidir. Daha fazla bilgi için C ++ 0x Wikipedia sayfasına göz atın.
LiKao

Biraz bulduğumda bunu yapacağım. C ++ 0X ile yakalamak için zaman sanırım
mkaes

Evet, C ++ 0x sallanıyor. GCC 4.5+ 'nin daha yaygın olmasını bekleyemem, bu yüzden lambdas kullanmaya başlayabilirim.
LiKao

5
İçin açıklama = deletetamamen doğru değil. = deleteherhangi bir işlev için kullanılabilir, bu durumda açıkça silindi olarak işaretlenir ve herhangi bir kullanım bir derleyici hatasına neden olur. Özel üye işlevleri için bu, özellikle derleyici tarafından sizin için oluşturulmadıkları anlamına gelir, ancak bu, = deletegerçekte ne olduğu değil, yalnızca silinmenin bir sonucudur .
MicroVirus

28

Gelen bu alıntı Dili [4th Edition] Programlama C ++ - Bjarne Stroustrup hakkında kitap görüşmelerini gerçek amaç kullanarak arkasında =delete:

3.3.4 İşlemleri Bastırma

Bir hiyerarşideki bir sınıfın varsayılan kopyasını veya hareketini kullanmak genellikle bir felakettir : bir tabana yalnızca bir işaretçi verildiğinde, türetilmiş sınıfın hangi üyelerine sahip olduğunu bilmiyoruz, bu yüzden bunları nasıl kopyalayacağımızı bilemeyiz . Bu nedenle, yapılacak en iyi şey genellikle varsayılan kopyalama ve taşıma işlemlerini silmek, yani bu iki işlemin varsayılan tanımlarını ortadan kaldırmaktır:

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();
    // ...
};

Şimdi, Derleyici tarafından bir Şekli kopyalama girişimi yakalanacaktır.

=deleteMekanizma herhangi operasyonu bastırmak için kullanılabilir, yani geneldir



5

Çalıştığım kodlama standartları, sınıf bildirimlerinin çoğunda aşağıdakilere sahipti.

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

Bu 6 taneden herhangi birini kullanırsanız, ilgili satırı yorumlamanız yeterlidir.

Örnek: sınıf FizzBus sadece dtor gerektirir ve bu nedenle diğer 5'i kullanmaz.

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

Burada sadece 1 yorum yapıyoruz ve başka yerde uygulanmasını kuruyoruz (muhtemelen kodlama standardının önerdiği yerlerde). Diğer 5'e (6) silme izni verilmiyor.

Farklı boyuttaki değerlerin örtülü tanıtımlarına izin vermemek için '= delete' seçeneğini de kullanabilirsiniz ... örnek

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

3

= deleteC ++ 11'de sunulan bir özelliktir. Buna göre =deletebu işlevi çağırmaya izin verilmez.

Detayda.

Bir sınıfta olduğunu varsayalım.

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

  }
};

Bu işlevi obj ataması için çağırırken buna izin verilmez. Atama operatörünün bir nesneden diğerine kopyalanmasını kısıtlayacağı anlamına gelir.



1

Silinmiş bir işlev dolaylı olarak satıriçi

(Mevcut cevaplara ek)

... Ve silinen bir işlev, işlevin ilk bildirimi olacaktır (işlev şablonlarının açık uzmanlıklarını silmek dışında - silme, uzmanlığın ilk bildiriminde olmalıdır), yani bir işlevi bildiremez ve daha sonra silemezsiniz, bir çeviri birimine yerel olarak tanımlanır.

Alıntı yapma [dcl.fct.def.delete] / 4 :

Silinen bir işlev dolaylı olarak satır içi. ( Not: Tek tanım kuralı ( [basic.def.odr] ) silinmiş tanımlara uygulanır. - son not ] Bir işlevin silinmiş tanımı, işlevin ilk bildirimi veya bir işlev şablonunun açıkça uzmanlaşması için , bu uzmanlığın ilk beyanı. [Örnek:

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

- son örnek )

Silinmiş bir tanım içeren birincil işlev şablonu özelleştirilebilir

Genel bir kural olsa da , uzmanlıklar aşırı yük çözünürlüğünün ilk adımına katılmadığı için işlev şablonlarını uzmanlaşmaktan kaçınmak olsa da, yararlı olabileceği bazı bağlamlar vardır. Örneğin, aşırı yüklenmemiş örtük olarak başka bir dönüşümle aşırı yüklemeye dönüştürülmesini istemeyecek tüm türleri eşleştirmek için hiçbir tanımı birincil işlev şablonu kullanıldığında; örn., tanımlanmamış, aşırı yüklenmemiş birincil işlev şablonunun açık uzmanlaşmasında kesin tür eşleşmeleri uygulayarak bir dizi örtük dönüşüm eşleşmesini örtük olarak kaldırmak.

C ++ 11'in silinen işlev kavramından önce, yalnızca birincil işlev şablonunun tanımını atlayarak bunu yapabilirdi, ancak bu , birincil işlev şablonunun yazarından hiçbir anlamsal niyet sağlamayan belirsiz tanımlanmamış referans hataları verdi (kasıtlı olarak atlandı) ?). Bunun yerine birincil işlev şablonunu açıkça silersek, uygun bir özel uzmanlığın bulunmaması durumunda hata iletileri çok daha hoş hale gelir ve ayrıca birincil işlev şablonunun tanımının ihmal / silinmesinin kasıtlı olduğunu gösterir.

#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< ...
}

Bununla birlikte, yukarıdaki birincil işlev şablonu için bir tanımı atlamak yerine, açık bir uzmanlaşma eşleşmediğinde belirsiz, tanımsız bir referans hatası vermek yerine, birincil şablon tanımı silinebilir:

#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; */
}

Silme amacının da açıkça görülebileceği daha okunabilir bir hata mesajı verir (burada tanımlanmamış bir referans hatası, geliştiricinin bunu düşünülmeyen bir hata olarak düşünmesine neden olabilir).

Neden bu tekniği kullanmak isteyelim ki? Yine, açık uzmanlıklar örtük dönüşümleri dolaylı olarak kaldırmak için yararlı olabilir .

#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; */
}

0

Bu, devralınan bir işlevi silebileceğiniz C ++ 0x standartlarında yeni bir şeydir.


11
Herhangi bir işlevi silebilirsiniz. Örneğin void foo(int); template <class T> void foo(T) = delete;, tüm örtük dönüşümleri durdurur. Yalnızca tür argümanları intkabul edilir, diğerleri "silindi" işlevini başlatmaya çalışır.
AmcaBens
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.