Bu, C ++ 'daki bir tuhaflık / özelliktir. Referansları tür olarak düşünmesek de, aslında bunlar yazım sisteminde "oturur". Bu garip görünse de (referanslar kullanıldığında, referans anlambiliminin otomatik olarak oluştuğu ve referansın "yoldan çıktığı" göz önüne alındığında), referansların dışında ayrı bir öznitelik olarak değil de tip sisteminde modellenmesinin savunulabilir nedenleri vardır. yazın.
İlk olarak, beyan edilen bir ismin her özniteliğinin tür sisteminde olması gerekmediğini düşünelim. C dilinden, "depolama sınıfı" ve "bağlantı" var. Gibi bir isim sokulabilir extern const int ri
burada, extern
statik depolama sınıfı ve bağlantı varlığı belirtir. Tip sadece const int
.
C ++, açık bir şekilde, ifadelerin tip sisteminin dışında niteliklere sahip olduğu fikrini benimser. Dil artık, bir ifadenin sergileyebileceği artan sayıdaki tip dışı öznitelikleri organize etme girişimi olan bir "değer sınıfı" kavramına sahiptir.
Yine de referanslar türlerdir. Neden?
Eskiden C ++ öğreticilerinde, türe sahip olarak const int &ri
sunulduğu ri
gibi bir bildirimin sunulduğu const int
, ancak semantiği referans aldığı açıklanırdı . Bu referans semantiği bir tür değildi; bu sadece ad ve depolama konumu arasında olağandışı bir ilişkiyi gösteren bir tür nitelikti. Dahası, referansların türler olmadığı gerçeği, tür oluşturma sözdizimi izin verse bile, neden referanslara dayalı türler oluşturamadığınızı rasyonelleştirmek için kullanıldı. Örneğin, mümkün olmayan referanslara diziler veya işaretçiler: const int &ari[5]
ve const int &*pri
.
Fakat gerçekte referansları olan türleri ve böylece decltype(ri)
olumlu olan bir referans türü düğümü alır. Temel alınan türe ulaşmak için tür ağacında bu düğümü geçmelisiniz remove_reference
.
Eğer kullandığınız zaman ri
, referans şeffaf böylece, çözüldükten ri
"gibi görünüyor ve hissediyor i
" ve bunun için bir "takma" denebilir. Tip sisteminde, ri
aslında " referans olan" bir türe sahiptir . const int
.
Referans türleri neden?
Referanslar tür değilse , bu işlevlerin aynı türe sahip olduğu kabul edilir:
void foo(int);
void foo(int &);
Bu, hemen hemen apaçık ortada olan nedenlerden dolayı olamaz. Aynı türe sahiplerse, bu, her iki tanımın da her iki tanım için uygun olacağı anlamına gelir.(int)
işlevin bir referans aldığından şüphelenilmesi gerekir.
Benzer şekilde, başvurular tür değilse, bu iki sınıf bildirimi eşdeğer olacaktır:
class foo {
int m;
};
class foo {
int &m;
};
Bir çeviri biriminin bir bildirimi kullanması ve aynı programdaki başka bir çeviri biriminin diğer bildirimi kullanması doğru olacaktır.
Gerçek şu ki, bir referans, uygulamada bir fark anlamına gelir ve bunu türden ayırmak imkansızdır, çünkü C ++ 'da tip, bir varlığın uygulanmasıyla ilgilidir: bitlerdeki "düzeni" tabiri caizse. İki işlev aynı türe sahipse, aynı ikili çağrı kurallarıyla çağrılabilirler: ABI aynıdır. İki yapı veya sınıf aynı türe sahipse, bunların düzeni ve tüm üyelere erişimin anlambilimiyle aynıdır. Referansların varlığı türlerin bu yönlerini değiştirir ve bu nedenle bunları tür sistemine dahil etmek basit bir tasarım kararıdır. (Bununla birlikte, burada bir karşı argümana dikkat edin: yapı / sınıf üyesi olabilirstatic
, bu da gösterimi değiştirir; ancak bu tür değildir!)
Bu nedenle, referanslar "ikinci sınıf vatandaşlar" olarak tür sistemindedir (ISO C'deki işlevler ve dizilerden farklı değildir). Referanslara işaretçi bildirmek veya bunların dizileri gibi referanslarla "yapamayacağımız" bazı şeyler vardır. Ama bu tip olmadıkları anlamına gelmez. Mantıklı bir şekilde tip değiller.
Tüm bu ikinci sınıf kısıtlamalar gerekli değildir. Referans yapıları olduğu göz önüne alındığında, referans dizileri olabilir! Örneğin
int x = 0, y = 0;
int &ar[2] = { x, y };
Bu sadece C ++ 'da uygulanmadı, hepsi bu. Referanslara işaretçiler hiçbir anlam ifade etmez, çünkü bir referanstan kaldırılan bir işaretçi sadece referans alınan nesneye gider. Referans dizisi olmamasının olası nedeni, C ++ insanlarının dizileri C'den miras alınan, onarılamaz birçok şekilde bozulmuş bir tür düşük seviyeli özellik olarak görmeleri ve dizilere dokunmak istememeleridir. yeni her şeyin temeli. Referans dizilerinin varlığı, referansların nasıl türler olması gerektiğine dair net bir örnek oluşturacaktır.
Olmayan-const
-qualifiable türleri: çok ISO C90 bulundu!
Bazı yanıtlar, referansların bir const
niteleyici almadığına işaret ediyor . Deklarasyon Çünkü, daha doğrusu kırmızı ringa const int &ri = i
bile değildir deneyen bir hale getirmek için const
ulaşım kolaylığı başvuru: (kendisi olmayan bir const nitelikli tip bir referans var const
). Tıpkı const in *ri
bir şeye işaretçi bildirdiği gibi const
, ancak bu işaretçi kendisi değildir const
.
Bununla birlikte, referansların const
niteleyiciyi kendilerinin taşıyamayacağı doğrudur .
Yine de bu o kadar tuhaf değil. ISO C 90 dilinde bile her tür olamaz const
. Yani diziler olamaz.
İlk olarak, bir const dizisini bildirmek için sözdizimi mevcut değildir: int a const [42]
hatalı.
Bununla birlikte, yukarıdaki beyannamenin yapmaya çalıştığı şey bir ara maddeyle ifade edilebilir typedef
:
typedef int array_t[42];
const array_t a;
Ama bu göründüğü gibi yapmıyor. Bu bildiride, değil mi a
aldığı const
nitelikli ama unsurlar! Yani, a[0]
bir const int
, ama a
sadece "int dizisi" dir. Sonuç olarak, bu bir teşhis gerektirmez:
int *p = a;
Bu yapar:
a[0] = 1;
Yine, bu, referansların diziler gibi tür sisteminde bir anlamda "ikinci sınıf" olduğu fikrinin altını çizer.
Diziler de referanslar gibi "görünmez bir dönüştürme davranışına" sahip olduklarından, analojinin daha da derin bir şekilde geçerli olduğuna dikkat edin. Programcı herhangi bir açık operatör kullanmak zorunda kalmadan, tanımlayıcı , sanki ifade kullanılmış gibi a
otomatik olarak bir int *
işaretçiye dönüşür &a[0]
. Bu, bir referansın ri
, onu birincil ifade olarak kullandığımızda i
, bağlı olduğu nesneyi sihirli bir şekilde ifade etme şekline benzer . Bu, "işaretçi bozunması dizisi" gibi başka bir "bozulma" dır.
Ve "işaretçi dizi" nin yanlış bir şekilde "dizilerin C ve C ++ 'da işaretçiler olduğunu" düşünerek çürümesi ile karıştırılmamamız gerektiği gibi, referansların da kendilerine ait hiçbir türü olmayan takma adlar olduğunu düşünmemeliyiz.
Zaman decltype(ri)
onun referan nesnesine referans olağan dönüşüm bastırır, bu çok farklı değildir sizeof a
dizi-to-pointer dönüşüm bastırılması ve işletim dizi türü kendisi boyutunu hesaplamak.
boolalpha(cout)
çok sıra dışı. Onunstd::cout << boolalpha
yerine yapabilirsin .