2016-04-19 14 views
8

, GCC 6 ve Clang 3.8 doğru davranıştır ne katılmıyorum:şablon nesnenin şablonu arkadaşı fonksiyonları ve ad aşağıdaki C++ örnek kodda

Bu yapmacık örneği "çalışır" - test() fonksiyonu içinde o.p verir gibi GCC. clang olarak, (tanımsız) işlevini get<int, int, float, double> çağırır:

template<typename ...Args> 
class obj { 
bool p = false; 

template<typename T, typename... Args2> 
friend T get(const obj<Args2...> &o) { return o.p; } 
}; 

template<typename T, typename... Args> 
T get(const obj<Args...> &o); 


bool test(const obj<int, float, double> &a) { 
return get<int>(a); 
} 

bir ad alanında aynı kodu koymak aynı şey çınlama yapar yapmak GCC neden olur.

namespace ns { 

template<typename ...Args> 
class obj { 
bool p = false; 

template<typename T, typename... Args2> 
friend T get(const obj<Args2...> &o) { return o.p; } 
}; 

template<typename T, typename... Args> 
T get(const obj<Args...> &o); 

} 

bool test(const ns::obj<int, float, double> &a) { 
return ns::get<int>(a); 
} 

https://godbolt.org/g/sWrXQO ve "doğru" ve genel bir şekilde orada derleyici

https://godbolt.org/g/9tIXwe ilan ve sonra ayrı olarak tanımlamak zorunda kalmadan bir arkadaşı üye şablon işlevi inline tanımlar. Bu işler gibi, geçerli:

struct Foo { 
friend bool bar() { return true; } // declares *and* defines a free function bar 
template<typename T> T bar2() { return true; } // doesn't work! 
}; 
+0

sen ... denemek yoksa çalışmıyor Çünkü 'şablon T olsun (const obj &o);' dışında sınıf – Jarod42

+0

ilan Neden:? Https://godbolt.org/g/peSscU –

+0

Evet, "T", "" değerini sağlamanız gerektiğinden, ADL çalışmaz (global kapsamda "get" şablonu olmadığı için): ADL'yi yeniden etkinleştirmek için kukla bir şablon almanızın (doğru eşleşmeyle) bildirilmesi: [Demo] (http://coliru.stacked-crooked.com/a/e40aaf90de712943) – Jarod42

cevap

5

sınıf şablonları tanımlanan friend fonksiyon şablonları ile ilgili iki çözülmemiş sorunlar vardır: 1545 ve 2174. Eski, geçerli olanın ne ölçüde geçerli olduğunu sorguluyor ve ikincisi, bu işlev şablonlarının gerçek örneklemelerine dayanarak ortaya çıkabilecek ihlal ihlalleri hakkında. Hangi derleyicinin doğru olduğundan emin değilim (daha önce her ikisinin de yanlış olduğuna inanmış olmakla birlikte), ancak standart olarak bu davranışta doğru davranışın ne olduğu konusunda yetersiz ya da zayıf olarak belirtilebilir.

ideal (sorun çözümünü bekliyor) derlemek gerekir kod:

template<typename ...Args> 
class obj { 
bool p = false; 

template<typename T, typename... Args2> 
friend T get(const obj<Args2...> &o) { return o.p; } 
}; 

template<typename T, typename... Args> 
T get(const obj<Args...> &o); 

friend beyan ilk get beyan, bu nedenle bu en iç kapsayan ad alanının yeni bir üye oluşturur: ::get. Harici bildirim sadece aynı işlevi tekrarlar, yani gerçekten sadece bir tane ::get. [Temp.over.link] Gönderen:

şablon parametrelerini içeren iki ifadeler eşdeğer kabul edilmektedir

eğer hariç, tek tanımlı kural (3.2) tatmin edecek ifadeleri içeren iki fonksiyon tanımları olduğunu adlandırmak için kullanılan jetonları şablon parametreleri, bir ifadede bir şablon parametresini adlandırmak için kullanılan bir belirteç olduğu sürece, diğer ifade içinde aynı şablon parametresini adlandıran başka bir jetonla değiştirilen farklı olabilir. İki bağımlı adın (14.6.2) eşdeğer olup olmadığını belirlemek için öğesinin belirlenmesi için, yalnızca şablonun adı dikkate alınmaz, şablon bağlamında ad arama sonucunun sonucudur. Farklı şablon parametre adları (Args2... vs Args...) kullanarak

gayet - fonksiyon şablonun ::get bu ikinci beyanı geçerlidir ve onu bulmak için arama için izin verir.

Bu bize getiriyor:

bool test(const obj<int, float, double> &a) { 
#ifdef UNQUAL 
    return get<int>(a);  // unqualified 
#else 
    return ::get<int>(a); // qualified 
#endif 
} 

bu gerektiği işin Hem - biz ad kapsamında olması işlevini redeclared beri, hatta ADL konusunda endişelenmenize gerek yoktur.Her iki çağrı da friend olan ::get<int>(obj<int, float, double>)'u bulmalı ve böylece kod derlenmeli ve bağlanmalıdır. gcc vasıfsız çağrıya izin verir, ancak nitelikli çağrıya izin vermez, clang ikisine de izin vermez.

Biz sadece değil sınıf içinde işlev şablonu tanımlayarak tamamen CWG sorunlarını hem sidestep: Bu formülasyon ile

template<typename ...Args> 
class obj { 
bool p = false; 

template<typename T, typename... Args2> 
friend T get(const obj<Args2...> &o); 
}; 

template<typename T, typename... Args> 
T get(const obj<Args...> &o) { return o.p; } 

, hem derler get ait nitelikli ve niteliksiz çağırma hem izin verir.


biz obj bir sınıf değil, bir sınıf şablonu olacak şekilde kod, ancak tüm şey eşit olduğunda yeniden edin:

class obj { 
    bool p = false; 

    template <class T> 
    friend T get(const obj& o) { return o.p; } 
}; 

template <class T> T get(const obj&); 

bool test(const obj& a) { 
#ifdef UNQUAL 
    return get<int>(a); 
#else 
    return ::get<int>(a); 
#endif 
} 

Her iki derleyiciler hem çağırmaları izin verir. Ancak, normal sınıf ve sınıf sınıfı olan obj arasındaki kurallarda farkında olduğum hiçbir fark yoktur.

+0

Kapsamlı yanıt için teşekkürler. Geçici çözümü (harici olarak tanımlamayı tanımlama) takdir ediyorum - ve bu zaten benim kodumda yaptığım şey. Bununla birlikte, templatize edilmemiş olan fonksiyonun inline olarak tanımlanabilmesi arasındaki uyumsuzluk (ve genellikle "arkadaş işletmecisi <<' ile yapılan) ve fonksiyonun kendisi ayarlandığı anda, deklarasyonunu ve tanımını ayırmak zorunda kaldığında ... sadece kafa karıştırıcı. Ama ... bu C++ :) –

+2

@MattG Harici şablon gereksinimi en iyi [bu yorum] 'da açıklanabilir (http://stackoverflow.com/questions/2953684/why-doesnt-adl-find-function-templates#comment29830195_2953783). Ayrıca, muhakemenin derleyiciler hakkında doğru olduğundan emin değilim. Başka birisinin burada bir noktada durmasını umuyoruz. – Barry

+1

CWG2174 ve CWG1545 ilgili olabilir. –

İlgili konular