2016-01-08 7 views
56

[except.ctor] में मानक (N4140) गारंटी देता है कि:फ़ंक्शन रिटर्न वैल्यू स्वचालित ऑब्जेक्ट्स हैं और इस प्रकार नष्ट होने की गारंटी है?

... विनाशकर्ता निर्माण के बाद से कोशिश ब्लॉक दर्ज किया गया था सभी स्वत: वस्तुओं के लिए लागू कर रहे हैं ...

हालांकि निम्नलिखित उदाहरण में खाली output साबित करता है कि फ़ंक्शन foo का वापसी मूल्य नष्ट नहीं हुआ है, हालांकि इसका निर्माण किया गया है। जी ++ (5.2.1) और क्लैंग ++ (3.6.2-1) और विकल्पों के साथ -O0 -fno-elide-constructors -std=c++14 का उपयोग करके संकलित।

struct A { ~A() { cout << "~A\n"; } }; 

struct B { ~B() noexcept(false) { throw 0; } }; 

A foo() { 
    B b; 
    return {}; 
} 

int main() { 
    try { foo(); } 
    catch (...) { } 
} 

यह एक बग दोनों ग्राम में ++ और बजना ++ है, या समारोह वापसी मान स्वचालित वस्तुओं नहीं माना जाता है, या यह सी ++ भाषा में एक पाश छेद है?

[stmt.return] में से कोई भी में, [expr.call] या [dcl.fct] मैं एक समारोह वापसी मान एक स्वत: वस्तु माना जाए या नहीं स्पष्ट कथन को खोजने के लिए सक्षम है।

... एक वापसी कथन निर्माण शामिल और कॉपी या एक अस्थायी वस्तु की चाल कर सकते हैं ...

और 5.2.2 p10: निकटतम संकेत मैंने पाया 6.3.3 p2 हैं :

एक समारोह कॉल एक lvalue यदि परिणाम प्रकार एक lvalue संदर्भ प्रकार या एक rvalue संदर्भ प्रकार कार्य करने के लिए, एक XValue अगर परिणाम प्रकार एक rvalue संदर्भ प्रकार आपत्ति उठाने का है, और एक prvalue अन्यथा ।

उत्तर

45

फ़ंक्शन रिटर्न मानों को अस्थायी माना जाता है, और वापसी मूल्य का निर्माण स्थानीय लोगों के विनाश से पहले अनुक्रमित किया जाता है।

दुर्भाग्य से, यह मानक में अनिर्धारित है। वहाँ एक open defect जो इस का वर्णन करता है और इस मुद्दे को

[...] प्रकार शून्य का एक संकार्य के साथ एक वापसी कथन केवल एक समारोह जिसका वापसी प्रकार सीवी शून्य है में इस्तेमाल किया जाएगा तय करने के लिए कुछ शब्दों प्रदान करता है। किसी अन्य ऑपरेंड के साथ एक रिटर्न स्टेटमेंट केवल उस फ़ंक्शन में उपयोग किया जाएगा जिसका रिटर्न टाइप सीवी शून्य नहीं है; रिटर्न स्टेटमेंट ऑपरेशन से कॉपी-प्रारंभिकरण (8.5 [dcl.init]) द्वारा वापस आने के लिए ऑब्जेक्ट या संदर्भ को प्रारंभ करता है। [...]

लौटाई गई इकाई की प्रति-प्रारंभिकरण वापसी सारणी के संचालन द्वारा स्थापित पूर्ण अभिव्यक्ति के अंत में अस्थायी के विनाश से पहले अनुक्रमित किया गया है, जो बदले में, स्थानीय चर के विनाश से पहले अनुक्रमित है (6.6 [stmt.jump]) ब्लॉक के रिटर्न स्टेटमेंट को संलग्न करते हुए।

चूंकि फ़ंक्शन रिटर्न मान अस्थायी हैं, इसलिए वे आपकी पोस्ट की शुरुआत में destructors are invoked for all automatic objects उद्धरण द्वारा कवर नहीं हैं। हालांकि, [class.temporary]/3 का कहना है:

[...] अस्थायी वस्तुओं पूर्ण अभिव्यक्ति का मूल्यांकन कि (lexically) बिंदु है जहां वे बनाए गए थे शामिल में अंतिम चरण के रूप में नष्ट कर रहे हैं। यह सच है भले ही वह मूल्यांकन अपवाद फेंकने में समाप्त हो। [...]

तो मुझे लगता है कि आप इसे जीसीसी और क्लैंग में एक बग पर विचार कर सकते हैं।

विनाशकर्ता से फेंक मत;)

+6

मैंने पाया कि दोनों जीसीसी और क्लैंग में पहले से ही इस बग के खिलाफ दायर किया गया है, इसलिए मुझे उम्मीद नहीं है कि वे जल्द ही इसे ठीक कर देंगे: [gcc] (https://gcc.gnu.org/bugzilla/show_bug.cgi आईडी = 3379 9), [क्लैंग] (https://llvm.org/bugs/show_bug.cgi?id=12286)। –

+0

क्या इस मामले में संकलक को ऑब्जेक्ट 'ए' के ​​निर्माण को छोड़ने की अनुमति है? मेरा मतलब आरवीओ के समान कुछ नियम है। – Mikhail

+0

@ मिखाइल मुझे विश्वास नहीं है। आरवीओ पूरी तरह से निर्माण को खत्म नहीं कर सकता है, यह * मध्यवर्ती * निर्माण को खत्म कर सकता है। – TartanLlama

7

यह एक बग है, और एक बार, एमएसवीसी वास्तव में यह सही हो जाता है: यह "~ ए" प्रिंट करता है।

+0

देखें कुछ समय बजना और जीसीसी कोड को खारिज कर दिया है, लेकिन MSVC परिभाषित व्यवहार के साथ कोड स्वीकार करता है। यह एक बार भी हुआ, अगर पूछा गया तो मैं उदाहरण प्रदान कर सकता हूं। –

+6

क्या आप मानक साबित कर सकते हैं कि एक फ़ंक्शन रिटर्न मान को स्वचालित ऑब्जेक्ट माना जाता है, या जो अन्यथा साबित करता है कि यह वास्तव में जीसीसी और क्लैंग में एक बग है? –

+0

@FlorianKaufmann मुझे लगता है [यह उदाहरण] (http://coliru.stacked-crooked.com/a/ee1a5d76230ec21c) दिखाता है कि कहीं 'ए' ऑब्जेक्ट है जिसके लिए विनाशक कभी नहीं बुलाया जाता है, भले ही यह गुंजाइश से बाहर हो बनने के बाद। – Antonio

7

मैंने आपके कोड को संशोधित किया और मुझे लगता है कि अब आउटपुट से हम देख सकते हैं कि ए नष्ट नहीं हुआ है।

#include<iostream> 

using namespace std; 

struct A { 
    ~A() { cout << "~A\n"; } 
    A() { cout << "A()"; } 
}; 

struct B { 
    ~B() noexcept(false) { cout << "~B\n"; throw(0); } 
    B() { cout << "B()"; } 
}; 

A foo() { 
    B b; 
    return; 
} 

int main() { 
    try { foo(); } 
    catch (...) {} 
} 

और उत्पादन होता है:

बी() ए() ~ बी

तो हाँ यह एक बग हो सकता है।

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