2013-08-20 2 views
12

क्लैंग द्वारा typename की जांच करने के लिए परीक्षण करते समय मैं इस अजीब व्यवहार में भाग गया। एमएसवीसी ने इसे खारिज करते हुए दोनों क्लैंग और जीसीसी इस कोड को स्वीकार करते हैं।क्या टाइपनाम को लाइन सदस्य परिभाषा के बाहर-विनिर्देशक में छोड़ा जा सकता है?

template<class T1> 
struct A 
{ 
    template<class T2> 
    struct B 
    { 
     static B f; 
     static typename A<T2>::template B<T1> g; 
    }; 
}; 

template<class T1> 
template<class T2> 
typename A<T2>::template B<T1> // ok, typename/template required 
    A<T1>::B<T2>::g; 

template<class T1> 
template<class T2> 
A<T1>::B<T2> // clang/gcc accept, msvc rejects missing typename 
    A<T1>::B<T2>::f; 

सामान्य तौर पर, एक योग्य-आईडी A<T1>::B<T2> (जहां A<T1> एक आश्रित नाम है) typename A<T1>::template B<T2> लिखा जाना चाहिए। क्या जीसीसी/क्लैंग का व्यवहार गलत है, या क्या इस विशेष मामले में सामान्य नियम (नीचे उद्धृत) का अपवाद है?

यह तर्क दिया जा सकता है कि A<T1> एक आश्रित नाम नहीं है, या B<T2> वर्तमान तत्कालता के सदस्य को संदर्भित करता है। हालांकि, प्रकार-विनिर्देशक को पार्स करने के बिंदु पर यह जानना संभव नहीं है कि वर्तमान तत्कालता A<T1> है। यह अनुमान लगाने के लिए समस्याग्रस्त लगता है कि A<T1> वर्तमान तात्कालिकता है।

14,6 नाम संकल्प [temp.res]

एक टेम्पलेट घोषणा या परिभाषा में इस्तेमाल एक नाम और है कि एक टेम्पलेट पैरामीटर पर निर्भर जब तक लागू नाम देखने पाता है एक प्रकार नाम नहीं मान लिया है कीवर्ड टाइपनाम द्वारा एक प्रकार का नाम या नाम योग्य है। टेम्पलेट विशेषज्ञता के

14.2 नाम [temp.names]

जब एक सदस्य टेम्पलेट विशेषज्ञता का नाम प्रदर्शित होता . या -> एक पोस्टफ़िक्स अभिव्यक्ति में या एक एक योग्य-आईडी में नेस्टेड-नाम-विनिर्देशक के बाद के बाद , और पोस्टफिक्स-अभिव्यक्ति की ऑब्जेक्ट या पॉइंटर अभिव्यक्ति या योग्य-आईडी में नेस्टेड-नाम-विनिर्देशक टेम्पलेट पैरामीटर (14.6.2) पर निर्भर करता है लेकिन वर्तमान तत्कालता (14.6) के सदस्य का संदर्भ नहीं देता है। 2.1), सदस्य टेम्पलेट नाम को कीवर्ड टेम्पलेट द्वारा उपसर्ग किया जाना चाहिए। अन्यथा नाम गैर-टेम्पलेट का नाम माना जाता है।

आगे की जांच करने के लिए क्या बजना यहाँ क्या कर रहा है, मैं भी कोशिश की इस:

template<class T1> 
struct C 
{ 
    template<class T2> 
    struct D 
    { 
     static typename A<T1>::template B<T2> f; 
     static typename A<T1>::template B<T2> g; 
    }; 
}; 

template<class T1> 
template<class T2> 
typename A<T1>::template B<T2> // ok, typename/template required 
    C<T1>::D<T2>::f; 

template<class T1> 
template<class T2> 
A<T1>::B<T2> // clang rejects with incorrect error 
    C<T1>::D<T2>::g; 

बजना error: redefinition of 'g' with a different type देता है, लेकिन g के प्रकार वास्तव में घोषणा से मेल खाता है।

मैं इसके बजाय typename या template के उपयोग का सुझाव देने वाले निदान को देखने की अपेक्षा करता हूं।

यह इस परिकल्पना को श्रेय देता है कि पहले उदाहरण में क्लैंग का व्यवहार अनपेक्षित है।

+0

मैं व्यक्तिगत रूप से जोड़ना होगा 'typename' ... लेकिन नहीं है मानक में अभी खोदने के लिए ड्राइव :) –

+0

@dribeas कोई चिंता नहीं;)। अब तक आप इन सभी भाषा वकील प्रश्नों से थक गए होंगे! – willj

+6

चाहे वह सही या गलत है, आप किसी भी * कोड * जीसी और क्लैंग स्वीकार करने के लिए सिर्फ एक अपवर्तनीय के लायक हैं, लेकिन वीसी ++ लापता 'टाइपनाम 'के आधार पर अस्वीकार कर देता है। –

उत्तर

1

क्लैंग और जीसीसी सही हैं।

कंपाइलर जानता है A<T1>::B<T2> एक प्रकार और B<T2> एक टेम्पलेट है और A<T1>::B<T2>::f वर्तमान तत्कालता का सदस्य है। इसलिए, typename और template कीवर्ड आवश्यक नहीं हैं।

v14.6.2 से।1p4:

एक नाम वर्तमान इन्स्टेन्शियशन का एक सदस्य है अगर यह

एक योग्य-आईडी, जिसमें नेस्टेड-नाम-विनिर्देशक वर्तमान इन्स्टेन्शियशन को संदर्भित करता है और है कि, जब ऊपर देखा, संदर्भित करता है वर्तमान इन्स्टेन्शियशन

A<T1>::B<T2> कम से कम एक सदस्य के लिए एक योग्य-आईडी और A<T1>:: नेस्टेड-नाम-विनिर्देशक जो वर्तमान इन्स्टेन्शियशन को संदर्भित करता है। हम जानते हैं कि A<T1>:: 14.6.2.1p1 से वर्तमान इन्स्टेन्शियशन को दर्शाता है:

एक नाम वर्तमान इन्स्टेन्शियशन को संदर्भित करता है अगर यह

है - में एक प्राथमिक वर्ग टेम्पलेट या एक सदस्य की परिभाषा एक प्राथमिक वर्ग टेम्पलेट की, <> (या एक बराबर टेम्पलेट अली में संलग्न (जैसा कि नीचे वर्णित) प्राथमिक टेम्पलेट की टेम्पलेट तर्क सूची के बाद वर्ग टेम्पलेट के नाम विशेषज्ञता के रूप में),

अपने कोड में, हम एक प्राथमिक वर्ग टेम्पलेट के एक सदस्य की एक परिभाषा है, यानी A<T1>::B<T2>::f है, और A<T1> प्राथमिक टेम्पलेट के टेम्पलेट तर्क सूची के बाद वर्ग टेम्पलेट का नाम है।

अपने प्रश्न में, आप However, at the point of parsing the type-specifier it's not possible to know that the current instantiation is A<T1> कहते हैं। हालांकि, मैं इसका पालन नहीं कर सकता क्योंकि A<T1> नाम ऊपर बताए अनुसार वर्तमान तत्कालता का संदर्भ देता है।

+0

नियम जिन्हें 'टाइपनाम' (और 'टेम्पलेट') की आवश्यकता होती है, पार्सर को यह जानना संभव बनाता है कि एक आश्रित नाम एक प्रकार या टेम्पलेट है या नहीं। पार्सर टोकन की एक धारा के माध्यम से आगे बढ़ता है, और उस बिंदु पर निर्णय लेना चाहिए कि नाम एक प्रकार (या एक टेम्पलेट) है जिस पर टोकन का सामना करना पड़ता है। यह तय करने के लिए कि ' :: बी'' टाइपनाम 'कीवर्ड के बिना क्लास टेम्पलेट का नाम है, तो पार्सर को यह पता लगाने के लिए कि' 'वर्तमान तात्कालिकता है, संभावित रूप से असीमित संख्या में टोकन को देखना होगा। – willj

+0

@ विल्ज: जबकि आपकी टिप्पणी का पहला भाग सही है, दूसरा नहीं है। नाम के कुछ रूप वर्तमान तात्कालिकता की पहचान करते हैं। इस मामले में 'ए ', और ':: ए 'दोनों वर्तमान तात्कालिकता का नाम देते हैं। संकलक वाक्यविन्यास द्वारा वर्तमान तत्कालता की पहचान करता है, उदाहरण के लिए 14.6.2.1p3 देखें। –

+0

देखें [यह उदाहरण] (http://coliru.stacked-crooked.com/view?id=3fe1c0107d63f27f28792d7678c32b3a-25dabfc2c190f5ef027f31d968947336) और त्रुटि संदेश देखें। कंपाइलर ** जानता है ** 'ए :: बी' एक क्लास टेम्पलेट नाम देता है क्योंकि यह पहले से ही इसे देखता है। –

1

एमएसवीसी सही है।

सी ++ 11 मानक के मेरे पढ़ने से पता चलता है कि typename आवश्यक है।

typename कीवर्ड के बिना, एक आश्रित नाम किसी प्रकार का नाम नहीं माना जाता है।

14,6 नाम संकल्प [temp.res]

2) एक नाम एक टेम्पलेट घोषणा या परिभाषा में इस्तेमाल किया और कहा कि एक टेम्पलेट पैरामीटर पर निर्भर जब तक लागू एक प्रकार नाम नहीं मान लिया है नाम लुकअप एक प्रकार का नाम पाता है या कीवर्ड टाइपनाम नाम से योग्यता प्राप्त करता है।

3) जब एक योग्य-आईडी एक प्रकार है कि वर्तमान इन्स्टेन्शियशन और उसके नेस्टेड-नाम-विनिर्देशक का सदस्य नहीं है का उल्लेख करने का इरादा है एक आश्रित प्रकार को संदर्भित करता है, यह कीवर्ड typename लगाया जाता किया जाएगा

7) एक वर्ग टेम्पलेट या एक वर्ग टेम्पलेट के एक सदस्य की परिभाषा के भीतर की परिभाषा के भीतर निम्नलिखित declarator-आईडी, कीवर्ड typename जब एक पहले के नाम की चर्चा करते हुए आवश्यक नहीं है घोषित वर्ग टेम्पलेट का सदस्य जो एक प्रकार की घोषणा करता है।[नोट: इस तरह के नाम अयोग्य नाम देखने, वर्तमान इन्स्टेन्शियशन, या वर्ग के सदस्य पहुँच अभिव्यक्ति देखने में वर्ग के सदस्य देखने का उपयोग कर जब वस्तु अभिव्यक्ति के प्रकार वर्तमान इन्स्टेन्शियशन

14.6.2.1 निर्भर है पाया जा सकता है प्रकार [temp.dep.type]

एक नाम वर्तमान इन्स्टेन्शियशन को संदर्भित करता है अगर यह

    एक प्राथमिक वर्ग टेम्पलेट की परिभाषा में
  • या एक प्राथमिक के एक सदस्य है वर्ग टेम्पलेट, प्राथमिक टेम्पलेट के टेम्पलेट तर्क सूची के बाद वर्ग टेम्पलेट के नाम (जैसा कि नीचे वर्णित) <>

में संलग्न A<T1>A के एक सदस्य की परिभाषा में प्रयोग किया जाता है , यह वर्तमान तत्काल को संदर्भित करता है। f की परिभाषा को पार्स करते समय A<T1>:: द्वारा योग्य प्रकार का नाम क्लास सदस्य नाम लुकअप वर्तमान तत्काल में पाया जा सकता है।

हालांकि, जब सी ++ पार्सर एक सदस्य फ़ंक्शन परिभाषा के रिटर्न-प्रकार में A<T1> से मुकाबला करता है - घोषणाकर्ता-आईडी से पहले - इसे अभी तक संलग्न वर्ग का नाम नहीं मिला है। पार्सर निर्धारित नहीं कर सकता कि A इस बिंदु पर संलग्न वर्ग को संदर्भित करता है या नहीं।

इस कारण से - चाहे A<T1> वर्तमान तत्कालता का नाम है या नहीं - मानक typename को घोषणाकर्ता-आईडी से पहले कक्षा टेम्पलेट के सदस्य की परिभाषा के भीतर छोड़ने की अनुमति नहीं देता है।

वॉन केटो द्वारा इस example यह दर्शाता है कि बजना/जीसीसी के व्यवहार असंगत है, और एक समान स्थिति में typename की आवश्यकता है:

template <typename T> 
struct A { 
    typedef int X; 
    X f(); 
}; 

template <typename T> 
A<T>::X A<T>::f() // error: missing 'typename' 
{ 
} 
संबंधित मुद्दे