2013-07-02 7 views
8

क्या यह कॉलर या कैली कॉपी या किसी फ़ंक्शन के रिटर्न वैल्यू को ले जा रहा है? उदाहरण के लिए, मेरे गुंजाइश गार्ड के नाशक सामने तत्व कॉपी करने के बाद कहा जाता है कि अगर मैं, एक कतार के पॉप() फ़ंक्शन को लागू करने के लिए इसकौन फ़ंक्शन के वापसी मूल्य की प्रतिलिपि बनाता है?

template <typename T> 
class queue 
{ 
    std::deque<T> d; 
public: 
    // ... // 
    T pop() 
    { 
     // Creates a variable whose destructor removes the first 
     // element of the queue if no exception is thrown. 
     auto guard = ScopeSuccessGuard([=]{ d.pop_front(); }); 
     return d.front(); 
    } 
} 

की तरह करना चाहते हैं?

संपादित करें: फ़ॉलो-अप प्रश्न: लाइन

auto item = q.pop(); 

अब दृढ़ता से अपवाद-सुरक्षित होगा?

+1

हां, स्वचालित भंडारण अवधि (जैसे आपके 'गार्डरहेयर) के साथ चर वापसी निर्देश (और इसके निर्माण/प्रतियां) के बाद नष्ट हो जाते हैं। आप कूद निर्देशों और घोषणाओं पर मानक भाग 6.6 और 6.7 की जांच कर सकते हैं। शायद आसान आप विनाश ^^ पर कुछ मुद्रित डमी वस्तुओं के साथ कोशिश कर सकते हैं – lip

उत्तर

9

स्थानीय चर के दायरे से बाहर होने से पहले वापसी मूल्य की प्रतिलिपि बनाई गई है। कॉपी/चाल एक अस्थायी स्थान (ढेर या रजिस्टर (ओं)) या सीधे कॉलर के अपने बफर या पसंदीदा रजिस्टरों के लिए हो सकती है - यह एक अनुकूलन/इनलाइनिंग समस्या है।

जहां एक अस्थायी स्थान शामिल है, कंपाइलर को कॉलर और कैली के बीच काम के कुछ विभाजन की व्यवस्था करनी चाहिए, और कई ओएस- और बाइनरी ऑब्जेक्ट/निष्पादन योग्य-प्रारूप-विशिष्ट सम्मेलन वापसी मूल्यों के लिए हैं (और पाठ्यक्रम के फ़ंक्शन पैरामीटर), जैसे कि एक कंपाइलर के साथ संकलित पुस्तकालय/वस्तुएं आमतौर पर किसी अन्य के साथ उपयोग की जा सकती हैं।

लाइन ...

auto item = q.pop(); 

... दृढ़ता से अपवाद सुरक्षित होगा?

मान लिया जाये कि pop_front() नहीं throw कर सकते हैं, दिलचस्प मामले में जहां एक अस्थायी स्थान समारोह का लौटे के बाद, दिया जाता है जहाँ से मूल्य फिर से फोन करने वाले बफर में बनाई है। ऐसा लगता है कि आपने इसके खिलाफ पर्याप्त रूप से संरक्षित नहीं किया है। एलिसन (कॉलर के परिणाम बफर/रजिस्टर (ओं) में सीधे वापसी मूल्य का निर्माण करने की अनुमति है, लेकिन इसकी आवश्यकता नहीं है।

इस का पता लगाने के लिए, मैं निम्नलिखित कोड लिखा है:

#include <iostream> 

struct X 
{ 
    X() { std::cout << "X::X(this " << (void*)this << ")\n"; } 
    X(const X& rhs) { std::cout << "X::X(const X&, " << (void*)&rhs 
           << ", this " << (void*)this << ")\n"; } 
    ~X() { std::cout << "X::~X(this " << (void*)this << ")\n"; } 

    X& operator=(const X& rhs) 
    { std::cout << "X::operator=(const X& " << (void*)&rhs 
       << ", this " << (void*)this << ")\n"; return *this; } 
}; 

struct Y 
{ 
    Y() { std::cout << "Y::Y(this " << (void*)this << ")\n"; } 
    ~Y() { std::cout << "Y::~Y(this " << (void*)this << ")\n"; } 
}; 

X f() 
{ 
    Y y; 
    std::cout << "f() creating an X...\n"; 
    X x; 
    std::cout << "f() return x...\n"; 
    return x; 
}; 

int main() 
{ 
    std::cout << "creating X in main...\n"; 
    X x; 
    std::cout << "x = f(); main...\n"; 
    x = f(); 
} 

g++ -fno-elide-constructors साथ संकलन, मेरे निर्गम (अतिरिक्त टिप्पणी के साथ) था:

creating X in main... 
X::X(this 0x22cd50) 
x = f(); main... 
Y::Y(this 0x22cc90) 
f() creating an X... 
X::X(this 0x22cc80) 
f() return x... 
X::X(const X&, 0x22cc80, this 0x22cd40) // copy-construct temporary 
X::~X(this 0x22cc80) // f-local x leaves scope 
Y::~Y(this 0x22cc90) 
X::operator=(const X& 0x22cd40, this 0x22cd50) // from temporary to main's x 
X::~X(this 0x22cd40) 
X::~X(this 0x22cd50) 

जाहिर है, काम f() के बाद हुआ बाएं दायरे: आपके स्कोप गार्ड (यहां वाई द्वारा प्रतिनिधित्व) के बाद से कोई भी अपवाद नष्ट हो गया था।

बात की ही तरह होता है अगर मुख्य X x = f(); या X x(f()); शामिल हैं, को छोड़कर यह प्रतिलिपि निर्माता कि f() -local चर के विनाश के बाद लागू किया है।

(मुझे सराहना है कि एक कंपाइलर का व्यवहार कभी-कभी मानक काम करने के लिए कुछ आवश्यक होने के बारे में तर्क के लिए एक खराब आधार है, लेकिन यह चारों ओर अन्य तरीकों से अधिक विश्वसनीय है: जब यह या तो उस कंपाइलर के टूटे हुए काम नहीं करता है - जो अपेक्षाकृत दुर्लभ है - या मानक को इसकी आवश्यकता नहीं है। यहां, कंपाइलर व्यवहार का उपयोग मानक की आवश्यकताओं के अपने प्रभाव में अनावश्यक वजन जोड़ने के लिए किया जाता है।)

उत्सुक के लिए बारीकियों विवरण: ऐसा नहीं है कि यह आम तौर पर कोड है कि सिर्फ एक ही रास्ता में कहा जा सकता है के लिए उपयोगी है, लेकिन कुछ है जो सुरक्षित हो सकता है const X& x = f(); है, के रूप में const संदर्भ अस्थायी के जीवनकाल फैली हुई है, लेकिन मैं खुद को यह समझाने में सक्षम नहीं हूं कि मानक को की अस्थायीता के लिए अस्थायी होने के लिए आवश्यक है, जिसका जीवनकाल अस्थायी कार्य है जो किसी भी अतिरिक्त प्रतिलिपि में कॉपी किया गया है; इसके लिए यह कितना छोटा है - यह मेरे कार्यक्रम में "काम करता है" और रोचक रूप से अस्थायी रूप से अस्थायी स्थान पर कब्जा कर लेता है, यदि रिटर्न वैल्यू को छोड़कर उपयोग किया जाता है, जो f() कोड को प्रभावी ढंग से संकलित करने की क्षमता के साथ संकलित किया गया है और -f-no-elide-constructors विकल्प इतना अक्षम नहीं है एक निराशा जोड़ने के तरीके से बाहर निकलने के रूप में एक ऑप्टिमाइज़ेशन: फ़ंक्शन को कॉल करने से पहले अस्थायी के लिए अतिरिक्त स्टैक स्पेस छोड़कर अस्थायी रूप से प्रतिलिपि बनाने के लिए अतिरिक्त कोड जोड़ना और स्टैक पॉइंटर को समायोजित करना ....

6

वापसी मूल्य की प्रतिलिपि कैली द्वारा की जाती है, और इसे विनाशकों के नाम से पहले किया जाना चाहिए, अन्यथा आप स्थानीय रूप से निर्मित चर के मूल्य/सामग्री को वापस नहीं कर सके।

यहाँ मानक के प्रासंगिक अनुभागों है: धारा 12.4, बिंदु 11 (विनाशकर्ता)

Destructors स्वत: भंडारण अवधि के साथ निर्माण किया वस्तुओं के लिए परोक्ष लागू कर रहे हैं

  • (3.7.3) जब ब्लॉक, जिसमें एक वस्तु बनाई गई है बाहर निकलता है (6,7)

मैं जहां यह सा एक जगह खोजने की कोशिश कर रहा था आईडी है कि "वापसी विनाश से पहले होती है", लेकिन यह स्पष्ट नहीं करता है कि जैसा कि मैं चाहता हूं [जब तक कि मैं कुछ याद नहीं कर रहा हूं]।

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