2017-01-13 19 views
41

Clang bazı C++ 11 kod noktası g ++Takma ad şablonu neden çelişen bir bildirim veriyor?

template<class T> 
using value_t = typename T::value_type; 

template<class> 
struct S 
{ 
    using value_type = int; 
    static value_type const C = 0; 
}; 

template<class T> 
value_t<S<T>> // gcc error, typename S<T>::value_type does work 
const S<T>::C; 

int main() 
{  
    static_assert(S<int>::C == 0, ""); 
} 

herhangi g ++ sürümü için karşı Clang (sürüm svn gövde boyunca 3.1) için farklı bir davranış verir. yerine şablon takma value_t<S<T>> I dolu typename S<T>::value_type sonra g++ also works kullanırsanız ikincisi ben hataları

prog.cc:13:13: error: conflicting declaration 'value_t<S<T> > S< <template-parameter-1-1> >::C' 
const S<T>::C; 
      ^
prog.cc:8:29: note: previous declaration as 'const value_type S< <template-parameter-1-1> >::C' 
    static value_type const C = 0; 
          ^
prog.cc:13:13: error: declaration of 'const value_type S< <template-parameter-1-1> >::C' outside of class is not definition [-fpermissive] const S<T>::C; 

like this olsun.

Soru: Temel ifadeleriyle tamamen değiştirilebilir olması gereken şablon takma adlar değil mi? Bu bir g ++ hatası mı?

Güncelleştirme: Visual C++, sınıf dışı tanımdaki diğer ad şablonunu da kabul eder.

+4

Eşdeğer olması gerektiği gibi görünüyor: http://eel.is/c++draft/temp.alias#2 – Barry

+4

500 için derleyici hatası ile gideceğim, Alex – AndyG

+8

Ben o kadar önemsiz olduğunu düşünmüyorum . Takma adlara ilişkin bağımlı türlerin denkliği hakkında birçok soru var. Http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1979 ve bununla bağlantılı olan tüm konulara bakın. Bir cevap bu soruyu ve bu konuların konuyla ilgisini, IMO'yu kapsamalıdır. –

cevap

6

Sorun, SFINAE'ye dayanmaktadır. Eğer üye işlev value_t<S<T>> olmak yeniden Eğer dış beyanı gibi, o GCC mutlu derlemeyi yapacaktır: ifadesi şimdi işlevsel eşdeğerdir

template<class T> 
struct S 
{ 
    using value_type = int; 
    static const value_t<S<T>> C = 0; 
}; 

template<class T> 
const value_t<S<T>> S<T>::C; 

çünkü. değiştirilme hatası gibi şeyler alias-templates üzerinde oynamak gel, ama görebildiğiniz gibi, value_type const C üye işlevi value_t<S<T>> const S<T>::C olarak "prototype" olarak yok. Birincisi SFINAE'yi yapmak zorunda değilken, ikincisi bunu gerektirir. Açıkça, her iki deklarasyonun da farklı işlevleri vardır, dolayısıyla GCC'nin tantrumu. İlginçtir ki, Clang bir anormallik belirtisi olmadan derler. Ben de öyle düşünüyorum ki, Clang analizlerinin sırası GCC'lere kıyasla tersine çevrilir. Takma-şablon ifadesi çözüldükten ve iyi olduğunda (yani iyi biçimlendirilmişse), clang her iki bildirimi de karşılaştırır ve eşdeğer olduklarını kontrol eder (bu durumda, her iki ifade de value_type olarak çözülür).

Şimdi, standart gözlerden hangisi doğrudur? Bu, takma adın işlevselliğinin bir parçası olarak takma adın şablonunun SFNIAE'sini göz önünde bulundurup düşünmemesi hâlâ çözülmemiş bir sorundur. [temp.alias]/2 alıntı:

şablon kimliği bir takma şablonun uzmanlık belirtir

, bu tip-id şablon parametreleri için kalıptan bağımsız değişken ile yer değiştirmesi sonucu elde edilen ilişkili türü için eşdeğerdir takma ad şablonu. Diğer bir deyişle,

, bu iki eşdeğerdir: ikame gerçekleştirildikten sonra, her iki tip vector<int, Alloc<int>> olarak sona için

template<class T> 
struct Alloc { /* ... */ }; 

template<class T> 
using Vec = vector<T, Alloc<T>>; 

Vec<int> v; 
vector<int, Alloc<int>> u; 

Vec<int> ve vector<int, Alloc<int>> eşdeğer türleridir. "İkame sonrası" ifadesinin, denklemin sadece tüm şablon argümanlarının şablon parametreleri ile değiştirilmesinden sonra kontrol edilmesi anlamına geldiğini unutmayın. Yani, vector<T, Alloc<T>>'daki T, Vec<int>'dan int ile değiştirildiğinde, karşılaştırma başlar. Belki de bu Clang value_t<S<T>> ile yapıyor?Ama sonra [temp.alias]/3 Aşağıdaki alıntı var: şablon kimliği bağımlı olup olmadığını

Ancak, daha sonraki şablon argümanı ikamesi hala şablon id için de geçerlidir. [Örnek:

template<typename...> using void_t = void; 
template<typename T> void_t<typename T::foo> f(); 
f<int>(); // error, int does not have a nested type foo 

- uç örneği]: ifade iyi oluşturulmuş olması sahiptir, böylece derleyici ikame iyi olup olmadığını kontrol etmek için ihtiyaç

Burada sorun. Şablon argüman ikamesini (ör. typename T::foo) gerçekleştirmek için bir bağımlılık olduğunda, tüm ifadenin işlevselliği değişir ve "eşdeğerlik" tanımı farklıdır. Dış foo sitesindeki prototip iç bir işlevsel olarak farklı olduğu için

struct X 
{ 
    template <typename T> 
    auto foo(T) -> std::enable_if_t<sizeof(T) == 4>; 
}; 

template <typename T> 
auto X::foo(T) -> void 
{} 

: Örneğin, aşağıdaki kodu (GCC ve çınlama) derlenmeyecektir. Bunun yerine auto X::foo(T) -> std::enable_if_t<sizeof(T) == 4> yapmak, kodun düzgün bir şekilde derlenmesini sağlar. Bunun nedeni, foo dönüş türünün sizeof(T) == 4 sonucuna bağlı bir ifadedir, bu nedenle şablon değiştirilmesinden sonra, prototipinin her örneği farklı olabilir. Oysa, auto X::foo(T) -> void'un dönüş tipi asla farklı değildir, bu da X içindeki bildirimle çakışır. Bu, kodunuzda gerçekleşen aynı sorun. Yani GCC bu durumda doğru gibi görünüyor.

İlgili konular