नए कोड में C ++ 17 के [[नोडोडकार्ड]] का लगभग हर जगह उपयोग न करने का क्या कारण है?


70

C ++ 17 उस [[nodiscard]]विशेषता का परिचय देता है, जो प्रोग्रामर को फ़ंक्शन को इस तरह से चिह्नित करने की अनुमति देता है कि कंपाइलर चेतावनी देता है यदि एक लौटे ऑब्जेक्ट को कॉलर द्वारा त्याग दिया जाता है; एक ही विशेषता को संपूर्ण वर्ग प्रकार में जोड़ा जा सकता है।

मैंने मूल प्रस्ताव में इस विशेषता के लिए प्रेरणा के बारे में पढ़ा है , और मुझे पता है कि C ++ 20 में मानक कार्यों की विशेषता को जोड़ा जाएगा std::vector::empty, जिनके नाम वापसी मूल्य के बारे में एक अस्पष्ट अर्थ व्यक्त नहीं करते हैं।

यह एक शांत और एक उपयोगी विशेषता है। वास्तव में, यह लगभग बहुत उपयोगी लगता है । हर जगह [[nodiscard]], जिनके बारे में मैंने पढ़ा है , लोग इस पर चर्चा करते हैं जैसे कि आप इसे कुछ चुनिंदा कार्यों या प्रकारों में जोड़ देंगे और बाकी के बारे में भूल जाएंगे। लेकिन एक गैर-त्याग योग्य मूल्य एक विशेष मामला क्यों होना चाहिए, खासकर जब नया कोड लिखना हो? आमतौर पर बग या संसाधनों की बर्बादी के लिए एक वापसी वापसी मूल्य नहीं है?

और सी ++ के डिजाइन सिद्धांतों में से एक ही नहीं है कि कंपाइलर को यथासंभव त्रुटियों को पकड़ना चाहिए?

यदि ऐसा है, तो [[nodiscard]]लगभग हर एक गैर- voidफ़ंक्शन और लगभग हर एकल वर्ग प्रकार में अपने स्वयं के, गैर-विरासत कोड को क्यों नहीं जोड़ा जाए ?

मैंने अपने कोड में ऐसा करने की कोशिश की है, और यह ठीक काम करता है, सिवाय इसके कि यह बहुत बुरी तरह से क्रिया है कि यह जावा की तरह महसूस करना शुरू कर देता है। यह प्रतीत होता है और अधिक compilers बनाने के लिए प्राकृतिक डिफ़ॉल्ट रूप से खारिज कर दिया वापसी मूल्यों के बारे में चेतावनी दी है सिवाय कुछ अन्य मामलों के लिए जहां आप अपने इरादे का प्रतीक [*]

जैसा कि मैंने मानक प्रस्तावों, ब्लॉग प्रविष्टियों, स्टैक ओवरफ्लो प्रश्नों या इंटरनेट पर कहीं और इस संभावना के बारे में शून्य चर्चाएं देखी हैं, मुझे कुछ याद आ रहा होगा।

ऐसे मैकेनिक नए C ++ कोड में क्यों नहीं समझ पाएंगे? क्या वर्बोसिटी एकमात्र कारण नहीं है जो [[nodiscard]]लगभग हर जगह उपयोग नहीं करता है?


[*] सिद्धांत रूप में, आपके पास [[maydiscard]]इसके बजाय एक विशेषता जैसा कुछ हो सकता है , जो printfमानक-पुस्तकालय कार्यान्वयन जैसे कार्यों में पूर्वव्यापी रूप से जोड़ा जा सकता है ।


22
खैर वहाँ बहुत सारे कार्य हैं जहाँ रिटर्न वैल्यू को समझदारी से खारिज किया जा सकता है। operator =उदाहरण के लिए। और std::map::insert
इम्बिसिस

2
@ मिनीबिस: सच। मानक पुस्तकालय ऐसे कार्यों से भरा है। लेकिन मेरे अपने कोड में, वे अत्यंत दुर्लभ होते हैं; क्या आपको लगता है कि यह लाइब्रेरी कोड बनाम एप्लीकेशन कोड की बात है?
क्रिश्चियन हैकल

9
"... बहुत क्रिया है कि यह जावा की तरह लगने लगता है ..." - सी ++ के बारे में एक प्रश्न में इसे पढ़कर मुझे थोड़ा
गड़बड़ हो गया

3
@ChristianHackl टिप्पणी "भाषा को कोसने" के लिए सही जगह नहीं है, और निश्चित रूप से, जावा भी अन्य भाषाओं की तुलना में क्रिया है। लेकिन हेडर फाइलें, असाइनमेंट ऑपरेटरों, प्रतिलिपि निर्माणकर्ता और के समुचित उपयोग constएक अन्यथा "सरल" श्रेणी ( "सादे पुराने डेटा वस्तु" या बल्कि) ब्लोट कर सकते हैं काफी C ++।
मार्को 13

2
@ मार्को 13: मैं एक टिप्पणी में इतने सारे बिंदुओं को संबोधित नहीं कर सकता। आइए इसे संक्षिप्त करें: जावा में हेडर फाइलें नहीं हैं, जो फाइल काउंट को कम करता है, लेकिन युग्मन को बढ़ाता है; OTOH यह आपको अपनी फ़ाइल में प्रत्येक शीर्ष-स्तरीय कक्षा और प्रत्येक फ़ंक्शन को एक कक्षा में रखने के लिए मजबूर करता है। C ++ में, आप लगभग असाइनमेंट ऑपरेटरों को लागू नहीं करते हैं और स्वयं बिल्डरों की प्रतिलिपि बनाते हैं; वे फ़ंक्शंस मानक-पुस्तकालय कक्षाओं के लिए अधिक प्रासंगिक होते हैं , जैसे std::vectorया std::unique_ptr, जिनमें से आपको केवल अपनी कक्षा की परिभाषा में डेटा सदस्यों की आवश्यकता होती है। मैंने दोनों भाषाओं के साथ काम किया है; जावा एक ओकेजन भाषा है, लेकिन यह अधिक क्रिया है।
क्रिश्चियन हैकल

जवाबों:


73

नए कोड में, जो पुराने मानकों के अनुकूल नहीं होने चाहिए, उस विशेषता का उपयोग करें जहां भी यह समझदार हो। लेकिन C ++ के लिए, [[nodiscard]]एक खराब डिफ़ॉल्ट बनाता है। आप सलाह दें:

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

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

एक मौजूदा, परिपक्व भाषा के लिए एक बहुत बड़े कोड बेस के साथ डिजाइन निर्णय जरूरी हैं कि पूरी तरह से नई भाषा के लिए डिजाइन निर्णयों से अलग हो। यदि यह एक नई भाषा थी, तो डिफ़ॉल्ट रूप से चेतावनी समझदार होगी। उदाहरण के लिए, निम भाषा को स्पष्ट रूप से खारिज किए जाने के लिए अनावश्यक मानों की आवश्यकता होती है - यह एक C के साथ C ++ में प्रत्येक अभिव्यक्ति कथन को लपेटने के समान होगा (void)(...)

एक [[nodiscard]]विशेषता दो मामलों में सबसे उपयोगी है:

  • यदि किसी फ़ंक्शन का एक निश्चित परिणाम लौटने से परे कोई प्रभाव नहीं होता है, तो वह शुद्ध होता है। यदि परिणाम का उपयोग नहीं किया जाता है, तो कॉल निश्चित रूप से बेकार है। दूसरी ओर, परिणाम को छोड़ना गलत नहीं होगा।

  • यदि रिटर्न मान की जाँच की जानी चाहिए, उदाहरण के लिए C-like इंटरफ़ेस जो फेंकने के बजाय त्रुटि कोड देता है। यह प्राथमिक उपयोग का मामला है। मुहावरेदार सी ++ के लिए, यह काफी दुर्लभ है।

इन दो मामलों में अशुद्ध कार्यों का एक बड़ा स्थान छोड़ दिया जाता है जो एक मूल्य लौटाते हैं, जहां इस तरह की चेतावनी भ्रामक होगी। उदाहरण के लिए:

  • एक .pop()विधि के साथ एक कतार डेटा प्रकार पर विचार करें जो किसी तत्व को निकालता है और हटाए गए तत्व की एक प्रति लौटाता है। ऐसी विधि अक्सर सुविधाजनक होती है। हालांकि, कुछ ऐसे मामले हैं जहां हम केवल प्रतिलिपि प्राप्त किए बिना तत्व को निकालना चाहते हैं। यह पूरी तरह से वैध है, और एक चेतावनी सहायक नहीं होगी। एक अलग डिजाइन (जैसे std::vector) इन जिम्मेदारियों को विभाजित करता है, लेकिन इसमें अन्य ट्रेडऑफ़ हैं।

    ध्यान दें कि कुछ मामलों में, एक प्रतिलिपि वैसे भी बनाई जानी चाहिए ताकि आरवीओ को धन्यवाद वापस कॉपी मुक्त हो।

  • धाराप्रवाह इंटरफेस पर विचार करें, जहां प्रत्येक ऑपरेशन वस्तु को लौटाता है ताकि आगे के ऑपरेशन किए जा सकें। C ++ में, सबसे आम उदाहरण स्ट्रीम सम्मिलन ऑपरेटर है <<[[nodiscard]]प्रत्येक <<अतिभार को एक विशेषता जोड़ना बेहद बोझिल होगा ।

इन उदाहरणों से पता चलता है कि मुहावरेदार सी ++ कोड को चेतावनी के बिना "सी ++ 17 के साथ डिफ़ॉल्ट रूप से नोडकार्ड" के तहत संकलित करना भाषा काफी थकाऊ होगा।

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

यकीनन, यहाँ समस्या शब्दार्थ का परिवर्तन नहीं है, लेकिन यह है कि प्रत्येक C ++ मानक की विशेषताएँ प्रति-संकलन-इकाई के दायरे पर लागू होती हैं, न कि किसी प्रति-फ़ाइल क्षेत्र पर। यदि / जब कुछ भविष्य के C ++ मानक हेडर फ़ाइलों से दूर जाते हैं, तो ऐसा परिवर्तन अधिक यथार्थवादी होगा।


6
मैंने इसे उकेरा है, क्योंकि इसमें उपयोगी जानकारियां हैं। अभी तक यकीन नहीं है कि यह वास्तव में सवाल का जवाब देता है, हालांकि। पुनः अशुद्ध कार्य जैसे popया operator<<, वे स्पष्ट रूप से मेरी काल्पनिक [[maydiscard]]विशेषता द्वारा चिह्नित किए जाएंगे , इसलिए कोई चेतावनी उत्पन्न नहीं होगी। क्या आप कह रहे हैं कि इस तरह के फ़ंक्शंस आम तौर पर मेरे विश्वास करने की तुलना में बहुत अधिक सामान्य हैं, या मेरे स्वयं के कोडबेस की तुलना में बहुत अधिक सामान्य हैं? मौजूदा कोड के बारे में आपका पहला पैराग्राफ निश्चित रूप से सत्य है, लेकिन फिर भी मुझे आश्चर्य होता है कि क्या कोई और वर्तमान में अपने सभी नए कोड को लागू कर रहा है [[nodiscard]], और यदि नहीं, तो क्यों नहीं।
क्रिश्चियन हैकल

23
@ChristianHackl: C ++ के इतिहास में एक समय था, जहां मानों को कास्ट के रूप में वापस करने के लिए अच्छा अभ्यास माना जाता था। आप कह नहीं सकते returns_an_int() = 0, तो returns_a_str() = ""काम क्यों करना चाहिए ? C ++ 11 ने दिखाया कि यह वास्तव में एक भयानक विचार था, क्योंकि अब यह सभी कोड मूव्स को प्रतिबंधित कर रहा था क्योंकि मानों की कमी थी। मुझे लगता है कि उस पाठ से उचित दूर-दूर है "यह आपके ऊपर नहीं है कि आपके कॉलर आपके परिणाम के साथ क्या करते हैं"। परिणाम को अनदेखा करना एक पूरी तरह से वैध बात है जो कॉल करने वाले चाहते हैं, और जब तक आप यह नहीं जानते कि यह गलत है (बहुत उच्च बार), आपको इसे ब्लॉक नहीं करना चाहिए।
GManNickG

13
इस पर नामर्द empty()भी समझदार है क्योंकि नाम काफी अस्पष्ट है: क्या यह एक विधेय है is_empty()या यह एक उत्परिवर्ती है clear()? आप आमतौर पर इस तरह के उत्परिवर्ती से कुछ भी वापस करने की उम्मीद नहीं करेंगे, इसलिए रिटर्न वैल्यू के बारे में चेतावनी प्रोग्रामर को एक समस्या के लिए सचेत कर सकती है। स्पष्ट नाम के साथ, यह विशेषता उतनी आवश्यक नहीं होगी।
आमोन

13
@ChristianHackl: जैसा कि दूसरों ने कहा है, मुझे लगता है कि शुद्ध कार्यों का एक अच्छा उदाहरण है कि इसका उपयोग कब किया जाना चाहिए। मैं एक कदम आगे जाऊंगा और कहूंगा कि एक [[pure]]विशेषता होनी चाहिए, और यह होनी चाहिए [[nodiscard]]। इस तरह यह स्पष्ट है कि इस परिणाम को क्यों नहीं छोड़ना चाहिए। लेकिन शुद्ध कार्यों के अलावा, मैं अन्य प्रकार के कार्यों के बारे में नहीं सोच सकता, जिनमें यह व्यवहार होना चाहिए। और अधिकांश कार्य शुद्ध नहीं हैं, इसलिए मुझे नहीं लगता कि डिफ़ॉल्ट के रूप में एक सही विकल्प है।
GManNickG

5
@GManNickG: त्रुटि कोड को नजरअंदाज करने वाले कोड को डिबग करने में विफल और असफल होने के बाद, मैं दृढ़ता से मानता हूं कि त्रुटि कोड के विशाल बहुमत को डिफ़ॉल्ट रूप से जांचने की आवश्यकता है।
मूविंग डक

9

कारण मैं [[nodiscard]]लगभग हर जगह नहीं होगा :

  1. (प्रमुख :) यह मेरे हेडर में बहुत अधिक शोर का रास्ता पेश करेगा ।
  2. (प्रमुख :) मुझे ऐसा नहीं लगता कि मुझे अन्य लोगों के कोड के बारे में मजबूत अटकलें लगाई जानी चाहिए। आप मेरे द्वारा दिए गए वापसी मूल्य को छोड़ना चाहते हैं? ठीक है, अपने आप को खटखटाओ।
  3. (मामूली :) आप C ++ 14 के साथ असंगति की गारंटी देंगे

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


0

एक उदाहरण के रूप में: ऑपरेटर << का एक रिटर्न मान है जो कॉल पर निर्भर करता है या तो बिल्कुल आवश्यक है या बिल्कुल बेकार है। (std :: cout << x << y, पहली की जरूरत है क्योंकि यह धारा लौटाता है, दूसरा इसका कोई फायदा नहीं है)। अब प्रिंटफ के साथ तुलना करें जहां हर कोई रिटर्न वैल्यू को डिस्कस करता है जो कि एरर चेकिंग के लिए होता है, लेकिन तब ऑपरेटर << की कोई एरर चेकिंग नहीं होती है इसलिए यह पहली बार में उतना उपयोगी नहीं हो सकता है। तो दोनों ही मामलों में नोडकॉर्ड सिर्फ नुकसानदेह होगा।


यह एक प्रश्न का उत्तर देता है, जो यह नहीं पूछा गया था, " [[nodiscard]]हर जगह उपयोग न करने का क्या कारण है?"।
क्रिश्चियन हैकल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.