2017-02-20 18 views
19

क्लैंग 3.9 को अस्थायी संदर्भों का पता लगाने के लिए अस्थायी संदर्भों का पता लगाएं।अस्थायी

इस कोड को यूबी (सरलीकृत कोड) है:

template <class T> 
class my_optional 
{ 
public: 
    bool has{ false }; 
    T value; 

    const T& get_or_default(const T& def) 
    { 
     return has ? value : def; 
    } 
}; 

void use(const std::string& s) 
{ 
    // ... 
} 

int main() 
{ 
    my_optional<std::string> m; 
    // ... 
    const std::string& s = m.get_or_default("default value"); 
    use(s); // s is dangling if default returned 
} 

हम ऊपर की तरह कोड कुछ की टन है (my_optional यह वर्णन करने के लिए सिर्फ एक सरल उदाहरण है)।

यूबी के कारण 3.9 के बाद से सभी क्लैंग कंपाइलर इस स्मृति का पुन: उपयोग करना शुरू कर देते हैं, और यह वैध व्यवहार है।

सवाल यह है कि संकलन समय पर या रनटाइम पर सैनिटाइज़र जैसे कुछ खतरनाक संदर्भों का पता लगाने के लिए कैसे? कोई clang sanitizer उन्हें पहचान सकते हैं।

अद्यतन। कृपया उत्तर न दें: "std::optional का उपयोग करें"। ध्यान से पढ़ें: सवाल इसके बारे में नहीं है।
अपडेट 2। कृपया उत्तर न दें: "आपका कोड डिज़ाइन खराब है"। ध्यान से पढ़ें: प्रश्न कोड डिज़ाइन के बारे में नहीं है।

+0

@ सोप्रप्रोग्रामड्यूड धन्यवाद, मुझे पता है। बिंदु यह है: स्थिर नहीं है, लेकिन 'std :: string (" डिफ़ॉल्ट मान ") 'अंतर्निहित रूप से निर्मित है, यह एक अस्थायी है, और यह उस पंक्ति के बाद मर जाता है। तो अगली पंक्ति पर 's' लटक रहा है। लाइन के अंत में – vladon

+0

सं। स्ट्रिंग * अस्थायी * ऑब्जेक्ट _dies_। इसका संदर्भ लाइन 'उपयोग' पर लटक रहा है। – vladon

+1

@ सोप्रप्रोग्रामड्यूड "फ़ंक्शन कॉल में संदर्भ पैरामीटर के लिए एक अस्थायी बाध्य उस फ़ंक्शन कॉल वाली पूर्ण अभिव्यक्ति के अंत तक मौजूद है: यदि फ़ंक्शन एक संदर्भ देता है, जो पूर्ण अभिव्यक्ति को पार करता है, तो यह एक खतरनाक संदर्भ बन जाता है" http://en.cppreference.com/w/cpp/language/reference_initialization#Lifetime_of_a_temporary – vladon

उत्तर

22

आप एक अतिरिक्त अधिभार जोड़कर इस विशेष एपीआई के दुरुपयोग का पता लगाने कर सकते हैं:

const T& get_or_default(T&& rvalue) = delete; 

तर्क get_or_default करने के लिए दिया एक सच्चे rvalue है, तो इसके बजाय चुना जाएगा, इसलिए संकलन विफल हो जाएगा।

कार्यावधि में ऐसी त्रुटियों का पता लगाने के लिए के रूप में,/के साथ प्रयोग के बाद वापसी (ASAN_OPTIONS=detect_stack_use_after_return=1) बजना के AddressSanitizer उपयोग का प्रयास करें और या उपयोग के बाद गुंजाइश (-fsanitize-address-use-after-scope) का पता लगाने में सक्षम बनाया।

+0

हाँ! धन्यवाद! इन विकल्पों के साथ एएसन उन्हें पहचानता है :-) – vladon

3

यह एक दिलचस्प सवाल है। खतरनाक रेफरी का वास्तविक कारण यह है कि आप एक रावल्यू संदर्भ का उपयोग करते हैं जैसे कि यह एक लालसा था।

class my_optional 
{ 
public: 
    bool has{ false }; 
    T value; 

    const T& get_or_default(const T&& def) 
    { 
     throw std::invalid_argument("Received a rvalue"); 
    } 

    const T& get_or_default(const T& def) 
    { 
     return has ? value : def; 
    } 
}; 

इस तरह, अगर आप इसे एक अस्थायी करने के लिए एक रेफरी पारित (जो वास्तव में एक rvalue है):

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

class my_optional 
{ 
public: 
    bool has{ false }; 
    T value; 

    const T get_or_default(const T&& def) 
    { 
     return get_or_default(static_cast<const T&>(def)); 
    } 

    const T& get_or_default(const T& def) 
    { 
     return has ? value : def; 
    } 
}; 

एक और संभावना है पूछने के लिए बजना संकलक हैक करने होगा:

वैकल्पिक रूप से, आप अगर आप एक rvalue पारित किए गए एक अस्थायी मूल्य (और नहीं एक रेफरी) वापस जाने के लिए मजबूर कर उन्हें आसानी से ठीक की कोशिश कर सकते यह पता लगाने के लिए विधि एक lvalue या एक rvalue पारित कर दिया गया है या नहीं, से मैं पर्याप्त उन तकनीकों के लिए इस्तेमाल नहीं कर रहा हूँ ...

3

आप Explicit पुस्तकालय से lvalue_ref आवरण बाहर की कोशिश कर सकते। यह अवांछित बाध्यकारी को एक घोषणा में अस्थायी रूप से रोकता है, जैसे:

const T& get_or_default(lvalue_ref<const T> def) 
{ 
    return has ? value : def.get(); 
} 
संबंधित मुद्दे