Kodlama stili sonuçta özneldir ve önemli performans avantajlarının bundan kaynaklanması pek olası değildir. Ancak, liberal tek tip başlatmanın kullanımından kazandığınızı söyleyeceğim şey:
Yedekli Typenames'i En Aza İndirir
Aşağıdakileri göz önünde bulundur:
vec3 GetValue()
{
return vec3(x, y, z);
}
Neden vec3
iki kez yazmam gerekiyor ? Bunun bir anlamı var mı? Derleyici, fonksiyonun ne döndürdüğünü iyi ve iyi bilir. Neden sadece "bu değerlerle döndüğümün yapıcısını çağırın ve geri döndürün" diyemiyorum? Tek tip başlatma ile şunları yapabilirim:
vec3 GetValue()
{
return {x, y, z};
}
Herşey çalışıyor.
Daha da iyisi, fonksiyon argümanları içindir. Bunu düşün:
void DoSomething(const std::string &str);
DoSomething("A string.");
Bu bir yazım adı yazmak zorunda kalmadan çalışır, çünkü std::string
kendisini const char*
dolaylı olarak nasıl oluşturacağını bilir . Bu harika. Ama eğer o dize geldiyse, RapidXML de. Veya bir Lua ipi. Diyelim ki dize kadar olan uzunluğu gerçekten biliyorum. Sadece std::string
bir a const char*
iletirseniz, uzun süren bir kurucu dizenin uzunluğunu alır const char*
.
Açıkça uzun süren bir aşırı yük var. Ama kullanmak, bunu yapmak gerekir: DoSomething(std::string(strValue, strLen))
. Neden ekstra daktilo yazmış? Derleyici türünün ne olduğunu bilir. Tıpkı olduğu gibi auto
, fazladan daktilo isimleri kullanmaktan kaçınabiliriz:
DoSomething({strValue, strLen});
Sadece işe yarıyor. Yazım hatası yok, yaygara yok, hiçbir şey yok. Derleyici işini yapar, kod daha kısa ve herkes mutlu.
Verilen, ilk sürümün ( DoSomething(std::string(strValue, strLen))
) daha okunaklı olacağına dair iddialar var . Yani, neler olduğu ve kimin ne yaptığı açıktır. Bu, bir ölçüde doğrudur; tekdüze başlatma tabanlı kodun anlaşılması, işlev prototipine bakmayı gerektirir. Bazılarının const referansı olmadan parametreleri asla geçmemesi gerektiğini söylemesinin nedeni aynıdır: böylece bir değişkenin değişip değişmediğini görebilmek için arama sitesinde görebilirsiniz.
Fakat aynı şey söylenebilir auto
; Ne elde edeceğinizi bilmek auto v = GetSomething();
, tanımına bakmayı gerektirir GetSomething
. Ancak, buna auto
eriştikten sonra, bu umursamaz terkedilmiş vaziyette kullanılmayı durdurmadı . Şahsen, alıştıktan sonra iyi olacağını düşünüyorum. Özellikle iyi bir IDE ile.
Asla En Vexing Ayrıştırmasını Almayın
İşte bazı kodlar.
class Bar;
void Func()
{
int foo(Bar());
}
Pop sınavı: nedir foo
? Eğer bir değişkene cevap verdiyseniz yanılıyorsunuz. Aslında parametre olarak a döndüren bir işlevi alan bir işlevin prototipidir Bar
ve foo
işlevin dönüş değeri bir int'dir.
Buna C ++ 'ın "En Vexing Ayrıştırması" denir, çünkü kesinlikle bir insana hiç mantıklı gelmez. Fakat C ++ kuralları ne yazık ki bunu gerektiriyor: eğer bir işlev prototipi olarak yorumlanabilirse, o zaman olacaktır . Sorun şudur Bar()
; bu iki şeyden biri olabilir. Adında bir tür olabilir Bar
, bu geçici olduğu anlamına gelir. Veya parametre almayan ve a döndüren bir işlev olabilir Bar
.
Tek tip başlatma, bir fonksiyon prototipi olarak yorumlanamaz:
class Bar;
void Func()
{
int foo{Bar{}};
}
Bar{}
her zaman geçici oluşturur. int foo{...}
her zaman bir değişken oluşturur.
Kullanmak istediğiniz Typename()
ancak C ++ 'ın ayrıştırma kuralları nedeniyle yapamayacağınız birçok durum vardır . Ile Typename{}
, belirsizlik yoktur.
Olmama Nedenleri
Vazgeçtiğiniz tek gerçek güç daralmaktır. Tek tip başlatma ile daha küçük bir değeri başlatamazsınız.
int val{5.2};
Bu derlenmeyecek. Bunu eski moda başlatma ile yapabilirsiniz, ancak tek biçimli başlatma ile yapabilirsiniz.
Bu, başlatıcı listelerinin gerçekten çalışmasını sağlamak için yapıldı. Aksi takdirde, başlatıcı listelerinin türleriyle ilgili olarak pek çok belirsiz durum olacaktır.
Elbette, bazıları böyle bir kodun derlenmeyi hak etmediğini iddia edebilir . Şahsen kabul ediyorum; daraltmak çok tehlikelidir ve nahoş davranışlara yol açabilir. Derleyici aşamasında bu problemleri erkenden yakalamak muhtemelen en iyisidir. En azından, daralma birisinin kod hakkında çok fazla düşünmediğini gösteriyor.
Uyarı seviyeniz yüksekse, derleyicilerin sizi bu tür şeyler hakkında genellikle uyaracağını unutmayın. Yani gerçekten, tüm bunlar zorla yapılan bir hatayı uyarmaktır. Bazıları yine de yapmanız gerektiğini söyleyebilir.
Yapmamak için bir neden daha var:
std::vector<int> v{100};
Bu ne işe yarıyor? Bu bir yaratabileceği vector<int>
yüz varsayılan inşa öğelerle. Veya vector<int>
değeri 1 olan bir öğe yaratabilir 100
. Her ikisi de teorik olarak mümkün.
Gerçekte, ikincisini yapar.
Neden? Başlatıcı listeleri tek tip başlatma ile aynı sözdizimini kullanır. Bu yüzden belirsizlik durumunda ne yapılacağını açıklamak için bazı kurallar olmalıdır. Kural oldukça basittir: derleyici eğer olabilir bir bağ-başlatıldı listesi ile bir başlatıcı listesi yapıcı kullanmak, o zaman olacak . Yana vector<int>
götüren bir başlatıcı listesi yapıcı sahiptir initializer_list<int>
ve {100} geçerli olabilir initializer_list<int>
, bu nedenle olmalıdır .
Boyutlandırma yapıcısını almak için ()
yerine kullanmanız gerekir {}
.
Bunun vector
bir tam sayıya dönüştürülemeyen bir şey olsaydı, bunun olmayacağını unutmayın. Bir başlatıcı_ listesi, bu vector
türden başlatıcı liste yapıcısına uymaz ve bu nedenle derleyici diğer yapıcılardan seçim yapmakta serbesttir.