मुझे वास्तव में noexcept का उपयोग कब करना चाहिए?


507

noexceptकीवर्ड उचित रूप से कई समारोह हस्ताक्षर करने के लिए लागू किया जा सकता है, लेकिन मैं जब मैं अभ्यास में इसका उपयोग करने पर विचार करना चाहिए के रूप में लेकर अनिश्चित हूं। अब तक मैंने जो कुछ भी पढ़ा है, उसके आधार पर, noexceptकुछ महत्वपूर्ण मुद्दों को संबोधित करने के लिए अंतिम मिनट जोड़ लगता है जो कि मूव कंस्ट्रक्शन फेंकते हैं। हालाँकि, मैं अभी भी कुछ व्यावहारिक प्रश्नों के संतोषजनक उत्तर देने में असमर्थ हूं, जिसके कारण मुझे noexceptपहली बार में अधिक पढ़ना पड़ा।

  1. ऐसे कार्यों के कई उदाहरण हैं जो मुझे पता है कि वे कभी नहीं फेंकेंगे, लेकिन जिसके लिए कंपाइलर अपने दम पर इसे निर्धारित नहीं कर सकता है। क्या मुझे ऐसे सभी मामलोंnoexcept में फ़ंक्शन की घोषणा करनी चाहिए ?

    हर फंक्शन के ऐलान के noexceptबाद प्रोग्रामर प्रोडक्टिविटी (और सच में, गांड में दर्द होगा) बहुत कम हो जाएगा, इस बारे में सोचने की जरूरत है । मुझे किन स्थितियों के उपयोग के बारे में अधिक सावधान रहना चाहिए और किन स्थितियों के लिए मैं निहित हो सकता हूं ?noexceptnoexcept(false)

  2. जब मैं उपयोग करने के बाद वास्तव में प्रदर्शन में सुधार की उम्मीद कर सकता हूं noexcept? विशेष रूप से, कोड का एक उदाहरण दें जिसके लिए एक C ++ कंपाइलर के जोड़ के बाद बेहतर मशीन कोड उत्पन्न करने में सक्षम है noexcept

    व्यक्तिगत रूप से, मुझे इस बात की परवाह noexceptहै कि संकलक को प्रदान की गई स्वतंत्रता के कारण कुछ प्रकार के अनुकूलन को सुरक्षित रूप से लागू करना है। क्या आधुनिक संकलक noexceptइस तरह से लाभ उठाते हैं ? यदि नहीं, तो क्या मैं निकट भविष्य में उनमें से कुछ की उम्मीद कर सकता हूं?


40
कोड का उपयोग करता है move_if_nothrow(या whatchamacallit) एक प्रदर्शन सुधार देखेंगे अगर वहाँ एक noexcept चाल ctor है।
आर। मार्टिनो फर्नांडिस


5
यह है move_if_noexcept
निकोस

जवाबों:


180

मुझे लगता है कि इसके लिए "सर्वोत्तम प्रथाओं" का जवाब देना जल्दबाजी होगी क्योंकि अभ्यास में इसका उपयोग करने के लिए पर्याप्त समय नहीं है। अगर उन्हें बाहर निकलने के तुरंत बाद थ्रो स्पेसर्स के बारे में पूछा गया, तो जवाब अब बहुत अलग होंगे।

noexceptहर फंक्शन घोषणा के बाद प्रोग्रामर प्रोडक्टिविटी (और सच में, एक दर्द होगा) को कम करने के बाद मुझे इस बारे में सोचने की जरूरत है कि मुझे इसमें शामिल होने की जरूरत है या नहीं ।

खैर, तब इसका उपयोग करें जब यह स्पष्ट हो कि फ़ंक्शन कभी भी फेंक नहीं देगा।

जब मैं उपयोग करने के बाद वास्तव में प्रदर्शन में सुधार की उम्मीद कर सकता हूं noexcept? [...] व्यक्तिगत रूप से, मुझे इस बात की परवाह noexceptहै कि संकलक को प्रदान की गई स्वतंत्रता के कारण सुरक्षित रूप से कुछ प्रकार के अनुकूलन लागू होते हैं।

ऐसा लगता है कि सबसे बड़ा अनुकूलन लाभ उपयोगकर्ता के अनुकूलन से हैं, न कि संकलक की जाँच noexceptऔर उस पर अतिभारित होने की संभावना के कारण । अधिकांश कंपाइलर एक नो-पेनल्टी-इफ-यू-थ्रो-थ्रो अपवाद हैंडलिंग विधि का पालन करते हैं, इसलिए मुझे संदेह है कि यह आपके कोड के मशीन कोड स्तर पर बहुत (या कुछ भी) बदल जाएगा, हालांकि शायद द्विआधारी आकार को कम करके हैंडलिंग कोड।

का उपयोग करते हुए noexceptबिग फोर में (कंस्ट्रक्टर्स, काम, नहीं विनाशकर्ता के रूप में वे पहले से ही कर रहे हैं noexcept) होने की संभावना सबसे अच्छा सुधार के कारण के रूप में होगा noexceptचेकों इस तरह के रूप में टेम्पलेट कोड में 'आम' कर रहे हैं stdकंटेनरों। उदाहरण के लिए, std::vectorअपनी कक्षा की चाल का उपयोग तब तक नहीं करेंगे जब तक कि वह चिन्हित न हो जाए noexcept(या संकलक इसे अन्यथा हटा सकता है)।


6
मुझे लगता है कि std::terminateचाल अभी भी शून्य-लागत मॉडल का पालन करती है। यही है, यह सिर्फ इतना होता है कि noexceptफ़ंक्शन के भीतर निर्देशों की श्रेणी को कॉल करने के लिए मैप किया जाता है std::terminateयदि throwस्टैक अनइन्डर के बजाय इसका उपयोग किया जाता है। इसलिए मुझे संदेह है कि इसमें अधिक ओवरहेड है जो नियमित अपवाद ट्रैकिंग है।
मथिउ एम।

4
@ इसे देखें: stackoverflow.com/a/10128180/964135 वास्तव में इसे केवल गैर-फेंकना होगा, लेकिन इसकी noexceptगारंटी देता है।
Pubby

3
"यदि कोई noexceptफ़ंक्शन फेंकता है, तो std::terminateउसे बुलाया जाता है, जिसमें ऐसा लगता है कि इसमें थोड़ी मात्रा में ओवरहेड शामिल होगा" ... नहीं, इसे ऐसे फ़ंक्शन के लिए अपवाद तालिका नहीं उत्पन्न करके लागू किया जाना चाहिए, जिसे अपवाद डिस्पैचर को पकड़ना चाहिए और फिर बाहर जमानत देनी चाहिए।
पोटाटोस्वाटर

8
@Pubby C ++ अपवाद हैंडलिंग आमतौर पर जंप टेबल को छोड़कर कोई ओवरहेड के साथ नहीं किया जाता है, जो संभावित रूप से कॉल साइट के पते को हैंडलर एंट्री पॉइंट्स पर फेंक रहा है। उन तालिकाओं को हटाना उतना ही करीब है जितना कि पूरी तरह से अपवाद को हटाने के लिए हो जाता है। एकमात्र अंतर निष्पादन योग्य फ़ाइल आकार है। शायद कुछ भी बताने लायक नहीं है।
पोटाटोस्वाटर

26
"ठीक है, तब इसका उपयोग करें जब यह स्पष्ट हो कि फ़ंक्शन कभी भी फेंक नहीं देगा।" मैं असहमत हूं। noexceptफ़ंक्शन के इंटरफ़ेस का हिस्सा है ; आपको इसे केवल इसलिए नहीं जोड़ना चाहिए क्योंकि आपका वर्तमान कार्यान्वयन फेंकने के लिए नहीं होता है। मुझे इस प्रश्न के सही उत्तर के बारे में निश्चित नहीं है, लेकिन मुझे पूरा विश्वास है कि आपके कार्य के साथ कैसा व्यवहार होता है इसका आज कोई लेना-देना नहीं है ...
निमो

133

जैसा कि मैं इन दिनों दोहराता रहता हूं: पहले शब्दार्थ

जोड़ना noexcept, noexcept(true)और noexcept(false)शब्दार्थ के बारे में सबसे पहले और सबसे महत्वपूर्ण है। यह केवल संयोग से संभावित अनुकूलन की संख्या को दर्शाता है।

एक प्रोग्रामर रीडिंग कोड के रूप में, की उपस्थिति noexceptइसके समान है const: यह मुझे बेहतर ग्रॉक में मदद करता है कि क्या हो सकता है या नहीं हो सकता है। इसलिए, यह सोचने में कुछ समय व्यतीत करने योग्य है कि क्या आप जानते हैं कि फ़ंक्शन फेंक देगा या नहीं। रिमाइंडर के लिए, किसी भी प्रकार की डायनामिक मेमोरी आवंटन फेंक सकते हैं।


ठीक है, अब संभव अनुकूलन पर।

सबसे स्पष्ट अनुकूलन वास्तव में पुस्तकालयों में किया जाता है। सी ++ 11 कई लक्षण प्रदान करता है जो यह जानने की अनुमति देता है कि फ़ंक्शन है noexceptया नहीं, और मानक लाइब्रेरी कार्यान्वयन स्वयं उन लक्षणों का उपयोग noexceptउपयोगकर्ता-परिभाषित वस्तुओं पर संचालन के पक्ष में करने के लिए करेगा, जो वे यदि संभव हो तो हेरफेर करते हैं। जैसे गतिमान शब्दार्थ

संकलक केवल, वसा (शायद) अपवाद डेटा को संभालने से का एक सा दाढ़ी सकता है क्योंकि यह है खाते में तथ्य यह है कि आप झूठ बोला हो सकता है लेने के लिए। यदि एक फ़ंक्शन चिह्नित noexceptकरता है, तो फेंक दिया std::terminateजाता है।

ये शब्दार्थ दो कारणों से चुने गए थे:

  • noexceptजब आश्रित पहले से ही इसका उपयोग नहीं करते हैं तब भी तुरंत लाभ (पिछड़े अनुकूलता)
  • noexceptजब कॉलिंग फ़ंक्शन के विनिर्देश को अनुमति देता है जो सैद्धांतिक रूप से फेंक सकता है, लेकिन दिए गए तर्कों के लिए अपेक्षित नहीं है

2
शायद मैं अनुभवहीन हूँ, लेकिन मैं एक ऐसे कार्य की कल्पना करूँगा जो केवल noexceptकार्य करता है , कुछ विशेष करने की आवश्यकता नहीं होगी, क्योंकि terminateइस स्तर पर पहुंचने से पहले कोई भी अपवाद ट्रिगर उत्पन्न हो सकता है। यह एक bad_allocअपवाद से निपटने और प्रचार करने के लिए बहुत भिन्न होता है।

8
हाँ, यह संभव है कि आप जिस तरह से सुझाव देते हैं उसी तरह से noexcept परिभाषित करें, लेकिन यह वास्तव में एक अनुपयोगी विशेषता होगी। कई फ़ंक्शन फेंक सकते हैं यदि कुछ शर्तों को पकड़ नहीं है, और आप शर्तों को पूरा करने के बावजूद भी उन्हें नहीं बुला सकते हैं। उदाहरण के लिए कोई भी फ़ंक्शन जो std :: अमान्य_argument फेंक सकता है।
tr3w

3
@MatthieuM। उत्तर के लिए थोड़ा देर से, लेकिन फिर भी। नोक्ससेप्ट के रूप में चिह्नित कार्य अन्य कार्यों को कह सकते हैं जो फेंक सकते हैं, वादा यह है कि यह फ़ंक्शन अपवाद नहीं छोड़ देगा अर्थात उन्हें केवल अपवाद को संभालना होगा!
माननीय

4
मैंने इस उत्तर को बहुत समय पहले दिया था, लेकिन इसके बारे में कुछ और पढ़ने और सोचने के बाद, मेरे पास एक टिप्पणी / प्रश्न है। "गति शब्दार्थ" एकमात्र उदाहरण है जिसे मैंने कभी भी किसी को भी देते देखा है जहाँ noexceptस्पष्ट रूप से सहायक / एक अच्छा विचार है। मैं निर्माण, चाल असाइनमेंट, और स्वैप के बारे में सोचना शुरू कर रहा हूं केवल ऐसे मामले हैं ... क्या आप किसी अन्य के बारे में जानते हैं?
निमो

5
@ नीमो: मानक पुस्तकालय में, यह संभवतः एकमात्र है, हालांकि यह एक सिद्धांत को प्रदर्शित करता है जिसे कहीं और पुन: उपयोग किया जा सकता है। एक चाल ऑपरेशन एक ऑपरेशन है जो अस्थायी रूप से कुछ राज्य को "लिम्बो" में डाल देता है, और केवल जब यह noexceptहो सकता है कि कोई इसे डेटा के एक टुकड़े पर आत्मविश्वास से उपयोग कर सकता है जिसे बाद में एक्सेस किया जा सकता है। मैं देख सकता था कि यह विचार कहीं और इस्तेमाल किया जा रहा है, लेकिन मानक लाइब्रेरी C ++ में पतली है और इसका उपयोग केवल उन तत्वों की प्रतियों को अनुकूलित करने के लिए किया जाता है जो मुझे लगता है।
Matthieu M.

77

यह वास्तव में कंपाइलर में ऑप्टिमाइज़र के लिए एक (संभावित) भारी अंतर करता है। कंपाइलर्स के पास वास्तव में फ़ंक्शन की परिभाषा के बाद खाली थ्रो () स्टेटमेंट के माध्यम से, साथ ही साथ प्रोप्राइटी एक्सटेंशन के माध्यम से यह सुविधा है। मैं आपको आश्वस्त कर सकता हूं कि आधुनिक संकलक बेहतर कोड उत्पन्न करने के लिए इस ज्ञान का लाभ उठाते हैं।

कंपाइलर में लगभग हर अनुकूलन किसी फ़ंक्शन के "फ्लो ग्राफ" का उपयोग करता है जो कि कानूनी है। प्रवाह प्रवाह में वे कार्य होते हैं जिन्हें आम तौर पर फ़ंक्शन के "ब्लॉक" कहा जाता है (कोड के क्षेत्र जिनके पास एक एकल प्रवेश और एक एकल निकास होता है) और ब्लॉक के बीच किनारों से संकेत मिलता है कि प्रवाह कहां तक ​​जा सकता है। Noexcept प्रवाह ग्राफ को बदल देता है।

आपने एक विशिष्ट उदाहरण के लिए कहा। इस कोड पर विचार करें:

void foo(int x) {
    try {
        bar();
        x = 5;
        // Other stuff which doesn't modify x, but might throw
    } catch(...) {
        // Don't modify x
    }

    baz(x); // Or other statement using x
}

इस फ़ंक्शन के लिए प्रवाह का ग्राफ अलग है अगर barलेबल किया गया है noexcept(अंत barऔर कैच स्टेटमेंट के बीच कूदने के लिए निष्पादन का कोई तरीका नहीं है )। जब लेबल किया जाता है noexcept, तो संकलक निश्चित होता है कि baz फ़ंक्शन के दौरान x का मान 5 है - x = 5 ब्लॉक को कहा जाता है कि bar()वह कैच स्टेटमेंट से किनारे के बिना baz (x) ब्लॉक को "हावी" करे ।

यह तब और अधिक कुशल कोड उत्पन्न करने के लिए "निरंतर प्रसार" नामक कुछ कर सकता है। यहाँ यदि baz इनलाइन है, तो x का उपयोग करने वाले कथनों में स्थिरांक भी हो सकते हैं और फिर रनटाइम मूल्यांकन के लिए जो उपयोग किया जाता है वह संकलन-समय मूल्यांकन में बदल सकता है, आदि।

वैसे भी, संक्षिप्त उत्तर: noexceptकंपाइलर एक तंग प्रवाह ग्राफ उत्पन्न करता है, और फ्लो ग्राफ का उपयोग सभी प्रकार के सामान्य संकलक अनुकूलन के कारण के लिए किया जाता है। एक कंपाइलर के लिए, इस प्रकृति के उपयोगकर्ता एनोटेशन कमाल के हैं। संकलक इस सामान का पता लगाने की कोशिश करेगा, लेकिन यह आमतौर पर नहीं हो सकता है (प्रश्न में फ़ंक्शन कंपाइलर को दिखाई नहीं देने वाली किसी अन्य ऑब्जेक्ट फ़ाइल में हो सकता है या कुछ फ़ंक्शन का उपयोग कर सकता है जो दिखाई नहीं देता है), या जब यह करता है, तो वहाँ है कुछ तुच्छ अपवाद जिन्हें फेंका जा सकता है, जिनके बारे में आपको पता भी नहीं है, इसलिए यह इसे स्पष्ट रूप से लेबल नहीं कर सकता है noexcept(स्मृति को आवंटित करना bad_alloc फेंक सकता है, उदाहरण के लिए)।


3
क्या यह वास्तव में अभ्यास में अंतर करता है? उदाहरण से वंचित है क्योंकि पहले कुछ भीx = 5 फेंक नहीं सकता है। यदि tryब्लॉक का वह हिस्सा किसी भी उद्देश्य से कार्य करता है तो तर्क धारण नहीं करेगा।
पोटाटोसवेटर

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

कंपाइलर कैसे पहचान सकता है कि कोड अपवाद फेंक सकता है? संभव अपवाद के रूप में माना जाता है और सरणी तक पहुंच है?
टॉमस क्यूब्स

3
@ qub1n यदि कंपाइलर फ़ंक्शन के शरीर को देख सकता है तो वह स्पष्ट throwविवरणों को देख सकता है , या अन्य चीजें जैसे newफेंक सकता है। यदि कंपाइलर शरीर को नहीं देख सकता है तो उसे उपस्थिति या अनुपस्थिति पर निर्भर होना चाहिए noexcept। सादा सरणी का उपयोग आम तौर पर अपवाद उत्पन्न नहीं करता है (C ++ में सीमा जाँच नहीं है) इसलिए नहीं, सरणी पहुंच अकेले संकलक के लिए फ़ंक्शन के अपवाद को सोचने का कारण नहीं बनेगी। (आउट-ऑफ-बाउंड एक्सेस यूबी है, गारंटीकृत अपवाद नहीं है।)
cdhowie

@ LCDhowie " इसे noexcept की उपस्थिति या अनुपस्थिति पर निर्भर होना चाहिए " या throw()पूर्व-noexcept C ++ की उपस्थिति
curiousguy

57

noexceptनाटकीय रूप से कुछ कार्यों के प्रदर्शन में सुधार कर सकते हैं। यह संकलक द्वारा मशीन कोड जनरेट करने के स्तर पर नहीं होता है, लेकिन सबसे प्रभावी एल्गोरिदम का चयन करके: जैसा कि दूसरों ने उल्लेख किया है, आप फ़ंक्शन का उपयोग करके यह चयन करते हैं std::move_if_noexcept। उदाहरण के लिए, की वृद्धि std::vector(जैसे, जब हम कॉल reserveकरते हैं) को एक मजबूत अपवाद-सुरक्षा गारंटी प्रदान करनी चाहिए। यदि यह पता है कि Tनिर्माण कंस्ट्रक्टर फेंक नहीं करता है, तो यह हर तत्व को स्थानांतरित कर सकता है। अन्यथा यह सभी को कॉपी करना होगा T। इसका वर्णन इस पोस्ट में विस्तार से किया गया है ।


4
परिशिष्ट: इसका मतलब है कि यदि आप मूव कन्स्ट्रक्टर्स को परिभाषित करते हैं या मूव असाइनमेंट ऑपरेटर्स noexceptउन्हें जोड़ते हैं (यदि लागू हो)! अवैध रूप से परिभाषित कदम सदस्य कार्यों ने noexceptउन्हें स्वचालित रूप से जोड़ा है (यदि लागू हो)।
मूकाहो

33

जब मैं उपयोग करने के बाद प्रदर्शन में सुधार के अलावा वास्तविक रूप से देख सकता हूं noexcept? विशेष रूप से, कोड का एक उदाहरण दें, जिसके लिए C ++ कंपाइलर noexcept के अलावा बेहतर मशीन कोड उत्पन्न करने में सक्षम है।

उम, कभी नहीं? कभी समय नहीं है? कभी नहीँ।

noexceptके लिए है संकलक एक ही तरीका है कि में प्रदर्शन अनुकूलन constसंकलक प्रदर्शन अनुकूलन के लिए है। यानी लगभग कभी नहीं।

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

जैसे move_if_noexceptकि पता चलता है कि अगर चाल निर्माणकर्ता के साथ परिभाषित किया गया है noexceptऔर प्रकार के const&बजाय वापस आ जाएगा &&यदि यह नहीं है। यह कहने का एक तरीका है कि अगर यह ऐसा करना बहुत सुरक्षित है।

सामान्य तौर पर, आपको इसका उपयोग करना चाहिए noexceptजब आपको लगता है कि ऐसा करने के लिए वास्तव में उपयोगी होगा । कुछ कोड अलग-अलग रास्तों को ले लेंगे यदि is_nothrow_constructibleउस प्रकार के लिए सही है। यदि आप उस कोड का उपयोग कर रहे हैं जो ऐसा करेगा, तो बेझिझक करेंnoexcept उपयुक्त बिल्डरों के ।

संक्षेप में: इसे मूव कंस्ट्रक्टर और समान कंस्ट्रक्शन के लिए उपयोग करें, लेकिन ऐसा महसूस न करें कि आपको इसके साथ पागल होना है।


13
सख्ती से, move_if_noexceptएक प्रति वापस नहीं होगी, यह एक रेवल्यू-रेफरेंस के बजाय एक कॉन्स्टल लवल्यू-रेफरेंस लौटाएगा। सामान्य तौर पर, जो कॉल करने वाले को एक चाल के बजाय एक प्रतिलिपि बनाने का कारण होगा, लेकिन move_if_noexceptप्रतिलिपि नहीं कर रहा है। अन्यथा, महान व्याख्या।
जोनाथन वेकली

12
+1 जोनाथन। एक वेक्टर का आकार बदलना, उदाहरण के लिए, वस्तुओं को स्थानांतरित करने के बजाय उन्हें स्थानांतरित करेगा यदि चाल निर्माणकर्ता है noexcept। इसलिए कि "कभी नहीं" सत्य नहीं है।
mfontanini

4
मेरा मतलब है, संकलक उस स्थिति में बेहतर कोड उत्पन्न करेगा । ओपी एक उदाहरण के लिए पूछ रहा है जिसके लिए कंपाइलर एक अधिक अनुकूलित अनुप्रयोग उत्पन्न करने में सक्षम है। यह मामला लगता है (भले ही यह एक कंपाइलर अनुकूलन नहीं है )।
mfontanini

7
@mfontanini: कंपाइलर केवल बेहतर कोड उत्पन्न करता है क्योंकि कंपाइलर एक अलग कोडपैथ को संकलित करने के लिए मजबूर होता है । यह केवल इसलिए काम करता है क्योंकि विभिन्न कोड std::vectorको संकलित करने के लिए संकलक को मजबूर करने के लिए लिखा गया है । यह कंपाइलर के बारे में कुछ पता लगाने के बारे में नहीं है; यह उपयोगकर्ता कोड के बारे में कुछ पता लगा रहा है।
निकोल बोलस

3
बात यह है, मैं आपके जवाब की भीख माँगने के लिए बोली में "संकलक अनुकूलन" खोजने के लिए प्रतीत नहीं कर सकते। जैसा कि @ChristianRau ने कहा, यह संकलक एक अधिक कुशल कोड उत्पन्न करता है, इससे कोई फर्क नहीं पड़ता कि उस अनुकूलन का मूल क्या है। सब के बाद, संकलक है एक अधिक कुशल कोड जनरेट, है ना? पुनश्च: मैंने कभी नहीं कहा कि यह एक कंपाइलर ऑप्टिमाइज़ेशन था, मैंने यहां तक ​​कहा कि "यह एक कंपाइलर ऑप्टिमाइज़ेशन नहीं है"।
mfontanini

22

में Bjarne के शब्द ( सी ++ प्रोग्रामिंग भाषा, 4 संस्करण , पेज 366):

जहां समाप्ति एक स्वीकार्य प्रतिक्रिया है, एक अनकहा अपवाद इसे प्राप्त करेगा क्योंकि यह समाप्ति () (.513.5.2.5) की कॉल में बदल जाता है। इसके अलावा, एक noexceptविनिर्देशक (.513.5.1.1) उस इच्छा को स्पष्ट कर सकता है।

सफल दोष-सहिष्णु प्रणाली बहुस्तरीय हैं। प्रत्येक स्तर कई त्रुटियों के साथ सामना करता है क्योंकि यह बहुत अधिक विपरीत हो सकता है और बाकी को उच्च स्तर पर छोड़ देता है। अपवाद उस दृश्य का समर्थन करते हैं। इसके अलावा, terminate()एक अपवाद प्रदान करके इस दृश्य का समर्थन करता है यदि अपवाद-हैंडलिंग तंत्र स्वयं दूषित है या यदि इसका अधूरा उपयोग किया गया है, तो इस प्रकार अपवाद छोड़ दिया जाता है। इसी तरह, noexceptत्रुटियों के लिए एक सरल बच प्रदान करता है जहां पुनर्प्राप्त करने की कोशिश करना असंभव लगता है।

double compute(double x) noexcept;     {
    string s = "Courtney and Anya";
    vector<double> tmp(10);
    // ...
}

वेक्टर निर्माता अपने दस डबल्स के लिए मेमोरी प्राप्त करने और ए को फेंकने में विफल हो सकता है std::bad_alloc। उस स्थिति में, प्रोग्राम समाप्त हो जाता है। यह बिना शर्त के समाप्त हो जाता है std::terminate()(.130.4.1.3)। यह कॉलिंग फंक्शंस से डिस्ट्रक्टर्स को इनवाइट नहीं करता है। यह कार्यान्वयन-परिभाषित है कि क्या throwऔर noexcept(जैसे, गणना में) () के बीच के स्कोप से विध्वंसक काटा जाता है। कार्यक्रम केवल समाप्त करने के बारे में है, इसलिए हमें किसी भी वस्तु पर निर्भर नहीं होना चाहिए। एक noexceptविनिर्देशक जोड़कर , हम इंगित करते हैं कि हमारा कोड एक थ्रो के साथ सामना करने के लिए नहीं लिखा गया था।


2
क्या आपके पास इस उद्धरण के लिए एक स्रोत है?
एंटोन गोलोव

5
@AntonGolov "C ++ प्रोग्रामिंग लैंग्वेज, 4th एडिशन" pg। 366
जंग खाए शेकलफोर्ड

यह मुझे लगता है जैसे मुझे वास्तव में जोड़ना चाहिए noexcept हर बार , सिवाय इसके कि मैं स्पष्ट रूप से अपवादों का ध्यान रखना चाहता हूं। आइए वास्तविक हों, अधिकांश अपवाद इतने अनुचित और / या इतने घातक हैं कि बचाया जाना शायद ही उचित या संभव है। उदाहरण के उद्धरण में, यदि आवंटन विफल रहता है, तो आवेदन शायद ही सही ढंग से काम करना जारी रख पाएगा।
नियॉनिट

21
  1. ऐसे कार्यों के कई उदाहरण हैं जिन्हें मैं जानता हूं कि वे कभी नहीं फेंकेंगे, लेकिन जिसके लिए कंपाइलर अपने दम पर इसे निर्धारित नहीं कर सकता है। क्या मुझे ऐसे सभी मामलों में फ़ंक्शन की घोषणा के लिए noexcept संलग्न करना चाहिए?

noexceptमुश्किल है, क्योंकि यह फ़ंक्शन इंटरफ़ेस का हिस्सा है। खासकर, यदि आप एक पुस्तकालय लिख रहे हैं, तो आपका ग्राहक कोड इस पर निर्भर कर सकता हैnoexcept संपत्ति है। बाद में इसे बदलना मुश्किल हो सकता है, क्योंकि आप मौजूदा कोड को तोड़ सकते हैं। यह चिंता का विषय हो सकता है जब आप कोड लागू कर रहे हैं जो केवल आपके आवेदन द्वारा उपयोग किया जाता है।

यदि आपके पास एक फ़ंक्शन है जो फेंक नहीं सकता है, तो अपने आप से पूछें कि क्या यह रहना पसंद करेगा noexceptया क्या भविष्य के कार्यान्वयन को प्रतिबंधित करेगा? उदाहरण के लिए, आप अपवादों (जैसे, इकाई परीक्षणों के लिए) को फेंककर अवैध तर्कों की त्रुटि जाँच शुरू कर सकते हैं, या आप अन्य पुस्तकालय कोड पर निर्भर हो सकते हैं जो इसके अपवाद विनिर्देश को बदल सकते हैं। उस मामले में, यह रूढ़िवादी और ओटिटिक होने के लिए सुरक्षित हैnoexcept

दूसरी ओर, यदि आप आश्वस्त हैं कि फ़ंक्शन को कभी नहीं फेंकना चाहिए और यह सही है कि यह विनिर्देश का हिस्सा है, तो आपको इसे घोषित करना चाहिए noexcept। हालांकि, यह ध्यान रखें कि कंपाइलर noexceptआपके कार्यान्वयन में बदलाव के उल्लंघन का पता लगाने में सक्षम नहीं होगा ।

  1. किन स्थितियों में मुझे noexcept के उपयोग के बारे में अधिक सावधान रहना चाहिए, और किन स्थितियों के लिए मैं निहित noexcept (झूठे) के साथ दूर हो सकता हूं?

कार्यों के चार वर्ग हैं जिन पर आपको ध्यान केंद्रित करना चाहिए क्योंकि वे संभवतः सबसे बड़ा प्रभाव डालेंगे:

  1. चालन चालें (चालन संचालक और चाल निर्माणकर्ता)
  2. स्वैप ऑपरेशन
  3. मेमोरी डीललॉकर (ऑपरेटर हटाएं, ऑपरेटर हटाएं]]
  4. विध्वंसक (हालांकि ये स्पष्ट रूप से noexcept(true)तब तक हैं जब तक आप उन्हें नहीं बनाते noexcept(false))

ये कार्य आम तौर पर होने चाहिए noexcept, और यह सबसे अधिक संभावना है कि पुस्तकालय कार्यान्वयन noexceptसंपत्ति का उपयोग कर सकते हैं। उदाहरण के लिए,std::vector मजबूत अपवाद की गारंटी के बिना गैर-फेंकने वाले चालन संचालन का उपयोग कर सकते हैं। अन्यथा, इसे कॉपी करने वाले तत्वों पर वापस गिरना होगा (जैसा कि C ++ 98 में हुआ था)।

इस तरह का अनुकूलन एल्गोरिथम स्तर पर है और संकलक अनुकूलन पर निर्भर नहीं करता है। यह एक महत्वपूर्ण प्रभाव डाल सकता है, खासकर यदि तत्व कॉपी करने के लिए महंगे हैं।

  1. जब मैं noexcept का उपयोग करने के बाद प्रदर्शन में सुधार के लिए वास्तविक रूप से उम्मीद कर सकता हूं? विशेष रूप से, कोड का एक उदाहरण दें, जिसके लिए C ++ कंपाइलर noexcept के अलावा बेहतर मशीन कोड उत्पन्न करने में सक्षम है।

noexceptकोई अपवाद विनिर्देश के खिलाफ लाभ या throw()यह है कि मानक कंपाइलरों को अधिक स्वतंत्रता की अनुमति देता है जब यह अनडिंडिंग स्टैक करने की बात आती है। में भीthrow() मामले में, कंपाइलर को स्टैक को पूरी तरह से खोलना होगा (और इसे ऑब्जेक्ट कंस्ट्रक्शन के सटीक रिवर्स ऑर्डर में करना होगा)।

में noexcept मामला है, दूसरे हाथ पर, यह है कि ऐसा करने के लिए आवश्यक नहीं है। इसमें कोई आवश्यकता नहीं है कि स्टैक को अनवाउंड करना है (लेकिन कंपाइलर को अभी भी इसे करने की अनुमति है)। यह स्वतंत्रता आगे कोड अनुकूलन की अनुमति देती है क्योंकि यह हमेशा ढेर को कम करने में सक्षम होने के उपरि को कम करती है।

Noexcept, stack unwinding और प्रदर्शन के बारे में संबंधित प्रश्न ओवरहेड के बारे में अधिक जानकारी में जाता है जब स्टैक अनइंडिंग की आवश्यकता होती है।

मैं स्कॉट मेयर्स की पुस्तक "प्रभावी मॉडर्न सी ++", "आइटम 14: डिक्लेयर फ़ंक्शंस नोएक्ससेप्ट की सलाह देता हूं अगर वे आगे पढ़ने के लिए अपवाद नहीं होंगे"।


फिर भी यह बहुत अधिक समझ में आता है यदि अपवाद जावा में सी ++ की तरह लागू किए गए थे, जहां आप विधि को चिह्नित करते हैं जो नकारात्मक के throwsबजाय कीवर्ड के साथ फेंक सकते हैं noexcept। मैं सिर्फ सी ++ डिजाइन विकल्पों में से कुछ नहीं मिल सकता है ...
डॉक

उन्होंने इसे नाम दिया noexceptक्योंकि throwपहले से ही लिया गया था। सीधे शब्दों में कहें throwइस्तेमाल किया जा सकता लगभग वे जिस तरह से आप का उल्लेख है, सिवाय इसके कि वे इसके बारे में डिजाइन असफल तो यह लगभग बेकार हो गया - हानिकारक भी। लेकिन हम इसके साथ अब फंस गए हैं क्योंकि इसे हटाने से थोड़ा फायदा होगा। तो noexceptमूल रूप से है throw_v2
AnorZaken

कैसे throwउपयोगी नहीं है?
जिज्ञासु २३'१

@curiousguy "थ्रो" खुद (अपवादों को फेंकने के लिए) उपयोगी है, लेकिन अपवाद के रूप में "फेंक" को हटा दिया गया है और C ++ 17 में भी हटा दिया गया है। अपवादों के कारण उपयोगी नहीं हैं, इस प्रश्न को देखें: stackoverflow.com/questions/88573/…
फिलिप क्लेमेन

1
@ PhilippCla theen throw()अपवाद निर्दिष्टकर्ता ने जैसी गारंटी प्रदान नहीं की nothrow?
जिज्ञासु

17

ऐसे कार्यों के कई उदाहरण हैं जिन्हें मैं जानता हूं कि वे कभी नहीं फेंकेंगे, लेकिन जिसके लिए कंपाइलर अपने दम पर इसे निर्धारित नहीं कर सकता है। क्या मुझे ऐसे सभी मामलों में फ़ंक्शन की घोषणा के लिए noexcept संलग्न करना चाहिए?

जब आप कहते हैं "मुझे पता है [वे] कभी नहीं फेंकेंगे", तो आप फ़ंक्शन के कार्यान्वयन की जांच करने से मतलब है जो आप जानते हैं कि फ़ंक्शन फेंक नहीं होगा। मुझे लगता है कि दृष्टिकोण बाहर है।

यह विचार करना बेहतर है कि क्या फ़ंक्शन फ़ंक्शन के डिज़ाइन का हिस्सा होने के लिए अपवाद फेंक सकता है : तर्क सूची के रूप में महत्वपूर्ण है और क्या कोई विधि उत्परिवर्ती (... const) है। यह घोषणा करते हुए कि "यह कार्य कभी भी अपवाद नहीं फेंकता है" कार्यान्वयन पर एक बाधा है। इसे स्वीकार करने का मतलब यह नहीं है कि फ़ंक्शन अपवाद फेंक सकता है; इसका मतलब है कि फ़ंक्शन का वर्तमान संस्करण और सभी भविष्य के संस्करण अपवाद फेंक सकते हैं। यह एक बाधा है जो कार्यान्वयन को कठिन बनाता है। लेकिन कुछ तरीकों में व्यावहारिक रूप से उपयोगी होने के लिए बाधा होनी चाहिए; सबसे महत्वपूर्ण बात, इसलिए उन्हें विध्वंसक से बुलाया जा सकता है, लेकिन यह भी कि मजबूत अपवाद गारंटी प्रदान करने वाले तरीकों में "रोल-बैक" कोड के कार्यान्वयन के लिए।


यह अब तक का सबसे अच्छा जवाब है। आप अपने तरीके के उपयोगकर्ताओं के लिए एक गारंटी बना रहे हैं, जो यह कहने का एक और तरीका है कि आप अपने कार्यान्वयन को हमेशा के लिए बाधित कर रहे हैं (सैंस ब्रेकिंग-चेंज)। ज्ञानवर्धक दृष्टिकोण के लिए धन्यवाद।
55 पर

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.