2017-01-13 4 views
41

बजना से कुछ सी ++ 11 कोड के बंदरगाह जी ++उपनाम टेम्पलेट एक विवादित घोषणा क्यों देता है?

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, ""); 
} 

बजना (संस्करण 3.1 SVN ट्रंक के माध्यम से) किसी भी जी ++ संस्करण के लिए बनाम के लिए अलग व्यवहार करने के लिए देता है। ओमकारा के लिए मैं त्रुटियों like this

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; 

मिलता है टेम्पलेट उर्फ ​​value_t<S<T>> के बजाय मैं पूरी typename S<T>::value_type तो g++ also works का उपयोग करें।

प्रश्न: टेम्पलेट उपनामों को उनकी अंतर्निहित अभिव्यक्ति के साथ पूरी तरह से अदला-बदली नहीं माना जाता है? क्या यह एक जी ++ बग है?

अद्यतन: दृश्य सी ++ कक्षा के बाहर की परिभाषा में उपनाम टेम्पलेट को भी स्वीकार करता है।

+4

निश्चित रूप से ऐसा लगता है कि वे समकक्ष होना चाहिए: http://eel.is/c++draft/temp.alias#2 – Barry

+4

मैं 500 के लिए कंपाइलर बग के साथ जाऊंगा, एलेक्स – AndyG

+8

मुझे नहीं लगता कि यह छोटा है । उपनाम टेम्पलेट्स के संबंध में निर्भर प्रकारों के समानता के बारे में बहुत सारे प्रश्न हैं। Http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1979 और इसके द्वारा जुड़े सभी मुद्दों को देखें। एक उत्तर में इस प्रश्न और आईएमओ पर उन मुद्दों की प्रासंगिकता शामिल होनी चाहिए। –

उत्तर

6

समस्या SFINAE पर निर्भर करती है। आप को फिर से लिखने यदि आपका सदस्य समारोह value_t<S<T>> होने के लिए, बाहर घोषणा की तरह है, तो जीसीसी खुशी से यह संकलन होगा:

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; 

क्योंकि अभिव्यक्ति अब कार्यात्मक बराबर है। प्रतिस्थापन विफलता जैसी चीजें उपनाम-टेम्पलेट्स पर खेलती हैं, लेकिन जैसा कि आप देखते हैं, सदस्य कार्य value_type const C में "प्रोटोटाइप" value_t<S<T>> const S<T>::C जैसा नहीं है। पहले व्यक्ति को SFINAE निष्पादित करने की आवश्यकता नहीं है, जबकि दूसरे को इसकी आवश्यकता होती है। तो स्पष्ट रूप से दोनों घोषणाओं में अलग-अलग कार्यक्षमता है, इसलिए जीसीसी का तंत्र है।

दिलचस्प बात यह है कि क्लैंग इसे असामान्यता के संकेत के बिना संकलित करता है। मुझे लगता है कि ऐसा ही होता है कि जीसीसी की तुलना में क्लैंग के विश्लेषण का आदेश उलट दिया जाता है। एक बार उपनाम-टेम्पलेट अभिव्यक्ति हल हो जाती है और ठीक है (यानी यह अच्छी तरह से गठित है), तो क्लैंग फिर दोनों घोषणाओं की तुलना करता है और जांचता है कि वे समकक्ष हैं (इस मामले में वे दोनों अभिव्यक्तियों को value_type पर हल करते हैं)।

अब, मानक की आंखों से कौन सा सही है? यह अभी भी एक अनसुलझा मुद्दा है कि क्या इसकी घोषणा की कार्यक्षमता के हिस्से के रूप में उपनाम-टेम्पलेट के SFNIAE पर विचार करें। [temp.alias]/2 का हवाला देते हुए:

एक टेम्पलेट आईडी एक उपनाम टेम्पलेट की विशेषज्ञता को दर्शाता है, यह के प्रकार के आईडी में टेम्पलेट मापदंडों के लिए अपने टेम्पलेट तर्कों के प्रतिस्थापन के द्वारा प्राप्त जुड़े प्रकार के बराबर है उपनाम टेम्पलेट।

दूसरे शब्दों में, इन दोनों बराबर हैं:

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

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

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

Vec<int> और vector<int, Alloc<int>>, बराबर प्रकार के होते हैं, क्योंकि के बाद प्रतिस्थापन किया जाता है, दोनों प्रकार के अंत vector<int, Alloc<int>> जा रहा है। ध्यान दें कि "प्रतिस्थापन के बाद" का अर्थ है कि समानता केवल तभी जांच की जाती है जब सभी टेम्पलेट तर्क टेम्पलेट पैरामीटर के साथ प्रतिस्थापित हो जाएं। यही है, तुलना Tvector<T, Alloc<T>> में intVec<int> से बदल दी गई है जब तुलना शुरू होती है। हो सकता है कि क्लेंग value_t<S<T>> के साथ क्या कर रहा है?लेकिन फिर [temp.alias]/3 से निम्नलिखित उद्धरण है:

हालांकि, यदि टेम्पलेट-आईडी निर्भर है, तो बाद में टेम्पलेट तर्क प्रतिस्थापन टेम्पलेट-आईडी पर लागू होता है। [उदाहरण:

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 

- अंत उदाहरण]

यहाँ समस्या है: अभिव्यक्ति है अच्छी तरह से गठित है, इसलिए संकलक कि क्या प्रतिस्थापन ठीक है की जाँच की जरूरत है। जब टेम्पलेट तर्क प्रतिस्थापन (उदा। typename T::foo) करने के लिए निर्भरता होती है, तो संपूर्ण अभिव्यक्ति की कार्यक्षमता में परिवर्तन होता है, और "समतुल्य" की परिभाषा अलग-अलग होती है। उदाहरण के लिए, निम्नलिखित कोड (जीसीसी और बजना) संकलन नहीं होगा:

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

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

क्योंकि बाहरी foo के प्रोटोटाइप भीतरी एक से कार्यात्मक रूप से अलग है। auto X::foo(T) -> std::enable_if_t<sizeof(T) == 4> करने के बजाय कोड को संकलित ठीक करता है। ऐसा इसलिए है क्योंकि foo का रिटर्न प्रकार एक अभिव्यक्ति है जो sizeof(T) == 4 के परिणाम पर निर्भर है, इसलिए टेम्पलेट प्रतिस्थापन के बाद, इसका प्रोटोटाइप इसके प्रत्येक उदाहरण से भिन्न हो सकता है। जबकि, auto X::foo(T) -> void का रिटर्न प्रकार कभी अलग नहीं है, जो X के अंदर घोषणा के साथ संघर्ष करता है। यह वही समस्या है जो आपके कोड के साथ हो रही है। इसलिए जीसीसी इस मामले में सही प्रतीत होता है।

संबंधित मुद्दे