2017-06-11 48 views
11

इस वर्ग पदानुक्रम पर विचार करें:सी ++ कंपाइलर अंतिम गति से इस गतिशील_कास्ट को अनुकूलित क्यों नहीं करते हैं?

struct Animal { virtual ~Animal(); }; 
struct Cat : virtual Animal {}; 
struct Dog final : virtual Animal {}; 

मेरे समझ class Dog पर final डाल सुनिश्चित करता है कि कि कोई भी कभी भी एक वर्ग Dog है, जो, परिणाम है, जिसका अर्थ है कि कोई भी कभी भी एक वर्ग है कि गया-एक ऐसा बना सकते हैं से इनहेरिट बना सकते हैं Dog और आईएस-ए Cat एक साथ।

पर विचार इन दोनों dynamic_cast रों:

Dog *to_final(Cat *c) { 
    return dynamic_cast<Dog*>(c); 
} 

Cat *from_final(Dog *d) { 
    return dynamic_cast<Cat*>(d); 
} 

जीसीसी, आईसीसी, और MSVC final क्वालीफायर ध्यान न दें और __dynamic_cast के लिए एक कॉल उत्पन्न; यह दुर्भाग्यपूर्ण है लेकिन आश्चर्य की बात नहीं है।

क्या मुझे आश्चर्य है कि बजना और Zapcc from_final के लिए दोनों generate इष्टतम कोड ("हमेशा nullptr वापसी"), लेकिन to_final के लिए __dynamic_cast के लिए एक कॉल उत्पन्न है।

यह वास्तव में एक चूक अनुकूलन अवसर (एक संकलक जहां स्पष्ट रूप से किसी डाले में final क्वालीफायर सम्मान में कुछ प्रयास डाल में) है, या कुछ सूक्ष्म कारण यह है कि मैं अभी भी नहीं कर रहा हूँ के लिए इस मामले में अनुकूलन असंभव है देख के?

+4

मेरा अनुमान है कि इस तरह की स्थिति अक्सर अधिकतर कंपाइलरों के बारे में चिंता करने के लिए पर्याप्त नहीं होती है। दक्षता के लिए एक सामान्य वास्तविक दुनिया की आवश्यकता के जवाब में अनुकूलन लागू किए जाते हैं। – cdhowie

+0

@cdhowie: आप सही हो सकते हैं; लेकिन मुझे क्या रोकता है यह है कि किसी ने स्पष्ट रूप से * से_फिनल 'मामले में क्लैंग अनुकूलन लिखने की परेशानी पर ध्यान दिया।'To_final' केस सममित है (विशेष रूप से कोडेजन के संदर्भ में, जहां यह दोनों प्रकार के लिए टाइपइन्फो खींच रहा है), फिर भी अज्ञात किसी ने * सममित ऑप्टिमाइज़ेशन नहीं जोड़ा है। "स्पष्ट रूप से आधा कार्यान्वित अनुकूलन" मेरे दिमाग में "बिल्कुल अनुकूलन नहीं" (सीएफ। जीसीसी, आईसीसी, एमएसवीसी) की तुलना में हमारे दिमाग में वीरडर दिखाई देता है। – Quuxplusone

उत्तर

3

ठीक है, मैं बजना के source code to find this method के माध्यम से खोदा:

/// isAlwaysNull - Return whether the result of the dynamic_cast is proven 
/// to always be null. For example: 
/// 
/// struct A { }; 
/// struct B final : A { }; 
/// struct C { }; 
/// 
/// C *f(B* b) { return dynamic_cast<C*>(b); } 
bool CXXDynamicCastExpr::isAlwaysNull() const 
{ 
    QualType SrcType = getSubExpr()->getType(); 
    QualType DestType = getType(); 

    if (const PointerType *SrcPTy = SrcType->getAs<PointerType>()) { 
    SrcType = SrcPTy->getPointeeType(); 
    DestType = DestType->castAs<PointerType>()->getPointeeType(); 
    } 

    if (DestType->isVoidType()) // always allow cast to void* 
    return false; 

    const CXXRecordDecl *SrcRD = 
    cast<CXXRecordDecl>(SrcType->castAs<RecordType>()->getDecl()); 

    //******************************************************************** 
    if (!SrcRD->hasAttr<FinalAttr>()) // here we check for Final Attribute 
    return false; // returns false for Cat 
    //******************************************************************** 

    const CXXRecordDecl *DestRD = 
    cast<CXXRecordDecl>(DestType->castAs<RecordType>()->getDecl()); 

    return !DestRD->isDerivedFrom(SrcRD); // search ancestor types 
} 

मैं कोड को पार्स से थोड़ी थक हो रही है, लेकिन यह मुझे नहीं लगता है कि आपके from_final अंतिम की वजह से बस हमेशा रिक्त नहीं है विशेषता, लेकिन इसके अतिरिक्त क्योंकि CatDog व्युत्पन्न रिकॉर्ड श्रृंखला में नहीं है। अनुमोदित, अगर इसमें final विशेषता नहीं थी, तो यह जल्दी से बाहर निकलती थी (जैसा कि Cat एक एसआरसी है), लेकिन यह हमेशा जरूरी नहीं होता।

मुझे लगता है कि इसके लिए कुछ कारण हैं। पहला यह है कि गतिशील_कास्ट रिकॉर्ड श्रृंखला को ऊपर और नीचे दोनों बनाता है। जब स्रोत रिकॉर्ड में अंतिम विशेषता होती है, तो यदि आप एक पूर्वजों (यदि स्रोत से कोई व्युत्पन्न कक्षा नहीं हो सकती है) तो आप केवल श्रृंखला की जांच कर सकते हैं।

लेकिन क्या होगा यदि कक्षा अंतिम नहीं है? मुझे संदेह है कि इसमें और भी कुछ हो सकता है। हो सकता है कि एकाधिक विरासत खोजों को नीचे की तुलना में अधिक कठिन बना देता है? डीबगर में कोड को रोक दिए बिना, मैं बस इतना कर सकता हूं।

यह मुझे पता है: isAlwaysNull एक प्रारंभिक निकास समारोह है। यह एक उचित दावा है कि यह साबित करने की कोशिश कर रहा है कि परिणाम हमेशा शून्य है। आप नकारात्मक साबित नहीं कर सकते (जैसा कि वे कहते हैं), लेकिन आप सकारात्मक को अस्वीकार कर सकते हैं।


शायद आप फ़ाइल के लिए Git इतिहास की जांच और जो व्यक्ति कि समारोह लिखा ईमेल कर सकते हैं। (या कम से कम final चेक जोड़ा गया)।

+1

गतिशील_कास्ट दस्तावेज़ों के अनुसार, यह ऊपर, नीचे और किनारे पर जा सकता है। मुझे संदेह है कि 'अंतिम' न केवल भविष्य में रहता है, बल्कि किनारे पर भी रोकता है। दोबारा, मुझे लगता है कि खोज विरासत रिकॉर्ड निषिद्ध रूप से महंगा हो सकता है (कम से कम इस छोटी जांच के लिए)। –

+0

अच्छा खुदाई! "पहला यह है कि गतिशील_कास्ट रिकॉर्ड श्रृंखला को ऊपर और नीचे दोनों बनाता है। जब स्रोत रिकॉर्ड में अंतिम विशेषता होती है, तो यदि आप एक पूर्वज हैं तो आप केवल श्रृंखला की जांच कर सकते हैं" - लेकिन ध्यान दें कि यदि * Dest * में अंतिम विशेषता है, तो गंतव्य * कुछ भी * का पूर्वज नहीं हो सकता है; उस मामले में किसी भी श्रृंखला की जांच करने की कोई आवश्यकता नहीं है। – Quuxplusone

+0

मैं कल रात थोड़ा थका हुआ था, और मैंने उस रेखा को फिर से लिखना जारी रखा। किसी भी घटना में, हाँ, आप सही हैं। मैं मदद नहीं कर सका लेकिन सोचने का एकमात्र कारण यह है कि चेक नहीं किया गया है 1) खोज समय या 2) चूक (जैसा आपने बताया)। चीयर्स! –

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