C ++ 11 silinmiş işlevler neden aşırı yük çözümlemesine katılıyor?


Yanıtlar:


117

= deleteSözdiziminin amacının yarısı, insanların belirli parametreleri belirli parametrelerle çağırmasını engelleyebilmektir. Bu, esas olarak belirli belirli senaryolarda örtük dönüştürmeleri önlemek içindir. Belirli bir aşırı yüklemeyi yasaklamak için, aşırı yük çözümlemesine katılması gerekir.

Verdiğiniz cevap size mükemmel bir örnek verir:

struct onlydouble {
  onlydouble(std::intmax_t) = delete;
  onlydouble(double);
};

Eğer deletefonksiyon tamamen kaldırılır, o yapacak = deleteBuna sözdizimi eşdeğer:

struct onlydouble2 {
  onlydouble2(double);
};

Bunu yapabilirsin:

onlydouble2 val(20);

Bu yasal C ++ 'dır. Derleyici tüm kuruculara bakacaktır; hiçbiri doğrudan bir tamsayı türü almaz. Ama içlerinden biri örtük bir din değiştirmeden sonra bunu alabilir. Yani buna diyecek.

onlydouble val(20);

Bu yasal C ++ değildir . Derleyici, deleted olanlar dahil tüm kuruculara bakacaktır . Tam bir eşleşme std::intmax_tgörecektir (herhangi bir tamsayı ile tam olarak eşleşecektir). Bu yüzden derleyici onu seçecek ve hemen bir hata verecektir, çünkü bir deleted fonksiyonu seçmiştir .

= delete"Bunu yasaklıyorum" demek sadece "Bu yok" demek değil. Bu çok daha güçlü bir ifade.

C ++ standardının neden "bu mevcut değil" yerine = sil "bunu yasaklıyorum" anlamına geldiğini soruyordum.

Çünkü "bu yok" demek için özel bir gramere ihtiyacımız yok. Bunu örtük olarak, söz konusu belirli "bunu" ilan etmeyerek elde ederiz. "Bunu yasaklıyorum" , özel dilbilgisi olmadan elde edilemeyen bir yapıyı temsil eder . Bu yüzden "bunu yasaklıyorum" demek için özel bir gramer alırız, diğerini değil.

Açık bir "bu yok" dilbilgisine sahip olarak kazanacağınız tek işlevsellik, birisinin daha sonra var olduğunu bildirmesini engellemek olacaktır. Ve bu kendi dilbilgisine ihtiyaç duyacak kadar kullanışlı değil.

Kopya oluşturucunun olmadığını beyan etmenin başka bir yolu yoktur ve varlığı anlamsız belirsizliklere neden olabilir.

Kopya yapıcısı özel bir üye işlevidir. Her sınıfın her zaman bir kopya oluşturucusu vardır. Her zaman bir kopya atama operatörüne, taşıma yapıcısına vb. Sahip oldukları gibi.

Bu işlevler mevcuttur; soru sadece onları çağırmanın yasal olup olmadığıdır. = deleteBunun var olmadıkları anlamına geldiğini söylemeye çalışırsanız , o zaman belirtimin bir işlevin var olmamasının ne anlama geldiğini açıklaması gerekir. Bu, spesifikasyonun ele aldığı bir kavram değildir.

Henüz bildirilmemiş / tanımlanmamış bir işlevi çağırmaya çalışırsanız, derleyici hata verecektir. Ancak tanımlanmamış bir tanımlayıcı nedeniyle hata verecektir , "işlev yok" hatası nedeniyle değil (derleyiciniz bunu bu şekilde bildirse bile). Çeşitli kurucuların tümü aşırı yük çözümü ile adlandırılır, bu nedenle "varoluşları" bu bağlamda ele alınır.

Her durumda, ya tanımlayıcı aracılığıyla bildirilen bir işlev ya da bir kurucu / yıkıcı vardır (aynı zamanda tanımlayıcı yoluyla da bildirilir, sadece bir tür tanımlayıcı). Operatör aşırı yükleme, tanımlayıcıyı sözdizimsel şekerin arkasına gizler, ancak yine de oradadır.

C ++ belirtimi, "var olmayan bir işlev" kavramını işleyemez. Bir aşırı yük uyumsuzluğunun üstesinden gelebilir. Aşırı yük belirsizliğini kaldırabilir. Ama orada ne olmadığını bilmiyor. Bu yüzden = delete, daha az yararlı olan "bu satırı hiç yazmamışmışım gibi davranmak" yerine çok daha yararlı olan "bu başarısızlığı çağırma girişimleri" açısından tanımlanır.

Ve yine ilk bölümü yeniden okuyun. Sen bunu yapamaz ile "fonksiyonu yok." Bu şekilde tanımlanmasının başka bir nedeni de budur: çünkü = deletesözdiziminin ana kullanım durumlarından biri , kullanıcıyı belirli parametre türlerini kullanmaya, açıkça çevirmeye vb. Zorlayabilmektir. Temel olarak, örtük tür dönüşümlerini engellemek için.

Öneriniz bunu yapmaz.


1
@Mehrdad: Neden = silme işleminin istediğiniz şekilde çalışmadığı konusunda daha fazla açıklamaya ihtiyacınız varsa, = sil'in sahip olması gerektiğini düşündüğünüz tam anlambilim konusunda daha net olmanız gerekir. "Bu satırı hiç yazmamışım gibi davranmak" mı olmalı? Yoksa başka bir şey mi olmalı?
Nicol Bolas

1
Hayır, demek = deleteistediğim "bu üye yok" demek istemiştim, bu da aşırı yük çözümüne katılamayacağı anlamına gelir.
user541686

6
@Mehrdad: Ve bu benim orijinal noktama geri dönüyor, bu yüzden onu gönderdim: eğer = delete"bu üye mevcut değil" demekse , o zaman gönderdiğim ilk örnek, insanların onlydoublekurucusuna tamsayı geçirmesini engelleyemezdi , çünkü onlydoublesilinen aşırı yük mevcut olmayacaktır . Aşırı yük çözümlemesine katılmaz ve bu nedenle tam sayıları geçmenizi engellemez. = deleteSözdiziminin amacının yarısı olan : "X'i bu işleve örtük olarak geçiremezsiniz" diyebilmek.
Nicol Bolas

3
@Mehrdad: Bu mantıkla, neden ihtiyacınız var =delete? Sonuçta, aynı şeyi yaparak "kopyalanamaz" diyebiliriz: kopya yapıcısını / atamayı özel ilan ederek. Ayrıca, bir şeyi özel olarak bildirmenin onu çağrılamaz hale getirmediğini unutmayın; sınıf içindeki kod yine de çağırabilir. Yani aynı değil = delete. Hayır, = deletesözdizimi daha önce oldukça uygunsuz ve anlaşılmaz olan bir şeyi çok daha açık ve makul bir şekilde yapmamıza izin veriyor.
Nicol Bolas

2
@Mehrdad: Çünkü ikincisi mümkün : buna "bunu bildirme" deniyor. O zaman var olmayacak. Özelliği kötüye kullanmak yerine bir aşırı yüklemeyi gizlemek için neden sözdizimine ihtiyacımız olduğuna gelince ... aynı etkiyi elde etmek için başka bir özelliği kötüye kullanmak yerine, neden bir şeyi açıkça ifade etme yoluna sahip olmamız gerektiğini gerçekten mi soruyorsunuz ? Neler olup bittiğini kodu okuyan herkes için daha açıktır. Kodu kullanıcı için daha kolay anlaşılır hale getirir ve yazmayı kolaylaştırırken, aynı zamanda geçici çözümdeki sorunları düzeltir. Biz yok gerek ya lambdas.
Nicol Bolas

10

C ++ Çalışma Taslağı 2012-11-02 bu kuralın arkasında bir mantık sunmuyor, sadece bazı örnekler

8.4.3 Silinen tanımlar [dcl.fct.def.delete]
...
3 [ Örnek : Varsayılan olmayan başlatma ve integral olmayan başlatma şu şekilde uygulanabilir:

struct onlydouble {  
  onlydouble() = delete; // OK, but redundant  
  onlydouble(std::intmax_t) = delete;  
  onlydouble(double);  
};  

- son örnek ]
[ Örnek : Bir sınıfın belirli yeni ifadelerde kullanılması, o sınıf için yeni kullanıcı tanımlı bir işlecin silinmiş tanımlarını kullanarak önlenebilir.

struct sometype {  
  void *operator new(std::size_t) = delete;  
  void *operator new[](std::size_t) = delete;  
};  
sometype *p = new sometype; // error, deleted class operator new  
sometype *q = new sometype[3]; // error, deleted class operator new[]  

- son örnek ]
[ Örnek : Kopyalama yapıcısının silinmiş tanımlarını ve kopya atama operatörünü kullanarak ve ardından taşıma yapıcısının ve taşıma atama operatörünün varsayılan tanımlarını sağlayarak bir sınıfı kopyalanamaz, yani yalnızca taşıma yapılabilir.

struct moveonly {  
  moveonly() = default;  
  moveonly(const moveonly&) = delete;  
  moveonly(moveonly&&) = default;  
  moveonly& operator=(const moveonly&) = delete;  
  moveonly& operator=(moveonly&&) = default;  
  ~moveonly() = default;  
};  
moveonly *p;  
moveonly q(*p); // error, deleted copy constructor  

- son örnek ]


4
Gerekçe örneklerden çok açık görünüyor, sence de öyle değil mi?
Jesse Good
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.