Kısa cevap: xBağımlı bir ad oluşturmak için, şablon parametresi bilinene kadar arama ertelenir.
Uzun cevap: bir derleyici bir şablon gördüğünde, template parametresini görmeden hemen belirli kontroller yapması beklenir. Diğerleri parametre bilinene kadar ertelenir. Buna iki fazlı derleme denir ve MSVC bunu yapmaz, ancak standart tarafından gereklidir ve diğer büyük derleyiciler tarafından uygulanır. İsterseniz, derleyici şablonu görür görmez derlemelidir (bir tür iç ayrıştırma ağacı temsiline) ve derlemeyi daha sonraya kadar derlemeyi ertelemelidir.
Belirli örneklemelerinden ziyade şablonun kendisinde gerçekleştirilen denetimler, derleyicinin şablondaki kodun dilbilgisini çözebilmesini gerektirir.
C ++ (ve C) 'de kod dilbilgisini çözmek için bazen bir şeyin tür olup olmadığını bilmeniz gerekir. Örneğin:
#if WANT_POINTER
typedef int A;
#else
int A;
#endif
static const int x = 2;
template <typename T> void foo() { A *x = 0; }
A bir türse, bir işaretçi bildirir (global gölgeden başka bir etkisi yoktur x). A bir nesne ise, bu çarpmadır (ve bir operatörün yasadışı aşırı yüklenmesine engel olur ve bir rvalue atar). Yanlışsa, bu hata faz 1'de teşhis edilmelidir , standart tarafından belirli bir örneklemede değil , şablonda bir hata olarak tanımlanır . Şablon hiçbir zaman somutlaştırılmasa bile, A bir ise int, yukarıdaki kod yanlış biçimlendirilmiş ve teşhis edilmesi gerekir, tıpkı foobir şablon olmasa bile , basit bir işlev gibi.
Artık standart , şablon parametrelerine bağlı olmayan isimlerin 1. aşamada çözülebilir olması gerektiğini söylüyor. ABurada bağımlı bir isim değil, türden bağımsız olarak aynı şeyi ifade ediyor T. Bu nedenle, 1. aşamada bulunup kontrol edilebilmesi için şablon tanımlanmadan önce tanımlanması gerekir.
T::AT'ye bağlı bir isim olurdu. 1. aşamada bunun bir tür olup olmadığını bilemeyiz. Sonunda Tbir örneklemede olduğu gibi kullanılacak olan tür henüz tam olarak tanımlanmamıştır ve öyle olsa bile, şablon parametrelerimiz olarak hangi türlerin kullanılacağını bilmiyoruz. Ancak, 1. Aşama, kötü biçimlendirilmiş şablonlar için kontroller yapmak için dilbilgisini çözmeliyiz. Bu yüzden standardın bağımlı isimler için bir kuralı vardır - derleyici , tür typenameolduklarını belirtmekle yetinmedikçe veya belirli belirsiz bağlamlarda kullanılmadıkça , tür olmadığını varsaymalıdır . Örneğin template <typename T> struct Foo : T::A {};, T::Abir temel sınıf olarak kullanılır ve bu nedenle açık bir şekilde bir türdür. Eğer Foobir veri elemanı vardır bazı tip örneğiA iç içe tip A yerine, örneklemede (aşama 2) kodda bir hata var, şablonda (aşama 1) bir hata değil.
Peki ya bağımlı bir temel sınıfa sahip bir sınıf şablonu?
template <typename T>
struct Foo : Bar<T> {
Foo() { A *x = 0; }
};
A bağımlı bir isim mi, değil mi? Temel sınıflarda, temel sınıfta herhangi bir ad görünebilir. Bu yüzden A'nın bağımlı bir isim olduğunu söyleyebiliriz ve ona tür olmayan olarak davranabiliriz. Bu, Foo'daki her adın bağımlı olduğu istenmeyen bir etkiye sahip olacaktır ve bu nedenle Foo'da kullanılan her türün (yerleşik türler hariç) nitelendirilmesi gerekir. Foo'nun içinde şunları yazmanız gerekir:
typename std::string s = "hello, world";
çünkü std::stringbağımlı bir ad olur ve bu nedenle aksi belirtilmedikçe tür olmayan olarak kabul edilir. Ah!
Tercih ettiğiniz kodun ( return x;) kullanılmasına izin verilmesinde ikinci bir sorun , daha Barönce tanımlanmış olsa Foove xbu tanımda üye olmasa bile , birisinin daha sonra bir tür Bariçin bir uzmanlık tanımlayabilmesidir Baz, bu şekilde Bar<Baz>bir veri üyesi olur xve Foo<Baz>. Bu örneklemede, şablonunuz genel öğeyi döndürmek yerine veri üyesini döndürür x. Ya da tersine, temel şablon tanımına sahip Barolsaydı x, onsuz bir uzmanlık tanımlayabilirler ve şablonunuz xgeri döndürülecek bir küresel arardı Foo<Baz>. Bence bunun, yaşadığınız sorun kadar şaşırtıcı ve üzücü olduğuna karar verildi, ama sessizce şaşırtıcı bir hata atmak yerine, şaşırtıcı.
Bu sorunlardan kaçınmak için yürürlükte olan standart, sınıf şablonlarının bağımlı temel sınıflarının açıkça talep edilmedikçe arama için dikkate alınmadığını söylüyor. Bu, her şeyin sadece bağımlı bir tabanda bulunabilmesi nedeniyle bağımlı olmasını engeller. Ayrıca gördüğünüz istenmeyen bir etkiye sahiptir - temel sınıftan bir şeyleri nitelemeniz gerekir veya bulunmaz. ABağımlı kılmanın üç yaygın yolu vardır :
using Bar<T>::A;sınıfta - Aşimdi buna Bar<T>bağlı olan bir şeye atıfta bulunuyor .
Bar<T>::A *x = 0;kullanım noktasında - Yine, Akesinlikle içeride Bar<T>. Bu, typenamekullanılmadığından beri çarpmadır , bu yüzden muhtemelen kötü bir örnek, ancak operator*(Bar<T>::A, x)bir değer döndürüp döndürmediğini öğrenmek için örneklemeye kadar beklememiz gerekecek . Kim bilir, belki de ...
this->A;kullanım noktasında - Abir üyedir, bu yüzden eğer değilse Foo, temel sınıfta olmalıdır, yine standart bunun bağımlı olduğunu söylüyor.
İki aşamalı derleme zor ve zordur ve kodunuzda ekstra ayrıntılar için bazı şaşırtıcı gereksinimler getirir. Fakat daha ziyade demokrasi gibi, muhtemelen diğerlerinden farklı olarak bir şeyler yapmanın en kötü yolu.
Örneğinizde , temel sınıfta iç içe bir tür return x;olup olmadığını anlamsız bir şekilde iddia edebilirsiniz x, bu nedenle dil (a) bunun bağımlı bir ad olduğunu ve (2) bunu tür olmayan olarak ele almasını ve kodunuz olmadan çalışır this->. Bir dereceye kadar, çözümden sizin durumunuzda geçerli olmayan bir soruna verilen teminat hasarının kurbanı olursunuz, ancak hala temel sınıfınızın potansiyel olarak altında küreselleri gölgeleyen ya da düşündüğünüz isimlere sahip olmayan isimler sunma sorunu var onlar vardı ve onun yerine küresel bir varlık bulundu.
Ayrıca muhtemelen varsayılan bağımlı isimlerin (her nasılsa bir nesne olarak belirtilmediği sürece türünü varsayıyorum) veya varsayılan (daha fazla bağlam duyarlı olması gerektiğini bunun için tersi olması gerektiğini iddia olabilir std::string s = "";, std::stringbaşka bir şey gramer yaptığı için bir tür olarak okunabilir anlam, std::string *s = 0;belirsiz olsa da). Yine, kuralların nasıl kabul edildiğini tam olarak bilmiyorum. Benim tahminim, gerekli olabilecek metin sayfası sayısının, bağlamların bir tür aldığı ve hangi türlerin türünün olmadığı birçok özel kural oluşturmaya karşı hafifletilmesidir.