सी ++ में दोहरा नकारात्मकता


124

मैं अभी बहुत बड़ी कोड बेस वाली परियोजना पर आया था।

मैं ज्यादातर C ++ के साथ काम कर रहा हूं और उनके द्वारा लिखे गए बहुत सारे कोड उनके बूलियन लॉजिक के लिए दोहरे नकार का उपयोग करते हैं।

 if (!!variable && (!!api.lookup("some-string"))) {
       do_some_stuff();
 }                                   

मुझे पता है कि ये लोग बुद्धिमान प्रोग्रामर हैं, यह स्पष्ट है कि वे दुर्घटना से ऐसा नहीं कर रहे हैं।

मैं सी + + विशेषज्ञ नहीं हूं, मेरा एकमात्र अनुमान है कि वे ऐसा क्यों कर रहे हैं, वे पूरी तरह से सकारात्मक बनाना चाहते हैं कि मूल्यांकन किया जा रहा मूल्य वास्तविक बूलियन प्रतिनिधित्व है। इसलिए वे इसे नकारते हैं, फिर इसे फिर से नकारते हैं ताकि इसे अपने वास्तविक बूलियन मूल्य पर वापस पा सकें।

क्या यह सही है, या मुझे कुछ याद आ रहा है?



इस विषय पर यहां चर्चा की गई है
दीमा

जवाबों:


121

यह बूल में बदलने की चाल है।


19
मुझे लगता है कि इसे (बूल) के साथ स्पष्ट रूप से डालना स्पष्ट होगा, इस ट्रिकी का उपयोग क्यों करें !!, क्योंकि यह कम टाइपिंग का है?
बैयान हुआंग

27
हालांकि, यह C ++ या आधुनिक C में व्यर्थ है, या जहां परिणाम केवल बूलियन अभिव्यक्ति में उपयोग किया जाता है (जैसा कि प्रश्न में है)। यह तब उपयोगी था जब हमारे पास कोई boolप्रकार नहीं था , बूलियन वेरिएबल्स के अलावा 1और मूल्यों को संचय करने में मदद करने के लिए 0
माइक सेमुर

6
@lzprgmr: स्पष्ट कास्ट MSVC पर "प्रदर्शन चेतावनी" का कारण बनता है । समस्या का उपयोग करना !!या !=0हल करना , और दोनों के बीच मैं पूर्व क्लीनर को खोजता हूं (क्योंकि यह अधिक मात्रा में काम करेगा)। इसके अलावा, मैं मानता हूं कि प्रश्न में कोड में उपयोग करने का कोई कारण नहीं है।
याकॉव गल्का

6
@ नोल्डोरिन, मुझे लगता है कि यह पठनीयता में सुधार करता है - यदि आप जानते हैं कि इसका क्या मतलब है, यह सरल, साफ और तार्किक है।
jwg

19
बढ़ाता है? खूनी नरक ... मुझे जो कुछ भी आप धूम्रपान कर रहे हैं दे।
नोल्डोरिन

73

यह वास्तव में कुछ संदर्भों में एक बहुत ही उपयोगी मुहावरा है। इन मैक्रोज़ (लिनक्स कर्नेल से उदाहरण) को लें। GCC के लिए, वे निम्नानुसार कार्यान्वित किए गए हैं:

#define likely(cond)   (__builtin_expect(!!(cond), 1))
#define unlikely(cond) (__builtin_expect(!!(cond), 0))

उन्हें ऐसा क्यों करना पड़ता है? जीसीसी __builtin_expectअपने मापदंडों को मानती है longऔर नहीं भी bool, इसलिए रूपांतरण का कुछ रूप होना चाहिए। चूंकि वे नहीं जानते हैं कि condजब वे उन मैक्रोज़ को लिख रहे हैं, तो !!मुहावरे का उपयोग करना सबसे सामान्य है ।

वे शायद 0 के खिलाफ तुलना करके भी ऐसा ही कर सकते हैं, लेकिन मेरी राय में, यह वास्तव में डबल-नेगमेंट करने के लिए अधिक सीधा है, क्योंकि यह एक कास्ट-टू-बूल के सबसे करीब है जो सी है।

इस कोड का उपयोग C ++ में भी किया जा सकता है ... यह एक निम्नतम-सामान्य-भाजक है। यदि संभव हो तो, C और C ++ दोनों में काम करें।


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

चूंकि SO मुझे अपनी टिप्पणी अपडेट करने की अनुमति नहीं देगा, इसलिए मैं अपनी गलती को ठीक करूंगा। एक बूलियन रूपांतरण में एक अभिन्न मूल्य परिणाम की उपेक्षा (यदि यह गैर-शून्य है, तो सच है अन्यथा)।
जोए कार्सन

51

कोडर्स सोचते हैं कि यह ऑपरेंड को बूल में बदल देगा, लेकिन क्योंकि && के ऑपरेंड पहले से ही स्पष्ट रूप से बूल में परिवर्तित हो चुके हैं, यह पूरी तरह से बेमानी है।


14
विजुअल C ++ इस ट्रिक के बिना कुछ मामलों में एक परफॉर्मेंस देता है।
किरिल वी। लयाडविंस्की

1
मुझे लगता है कि कोड में बेकार चेतावनी के आसपास काम करने की तुलना में सिर्फ चेतावनी को निष्क्रिय करना सबसे अच्छा होगा।
रुस्लान

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

12

यह लेखन से बचने की एक तकनीक है (चर! = 0) - अर्थात जो भी प्रकार से इसे एक बूल में बदलना है।

इस तरह के IMO कोड का सिस्टम में कोई स्थान नहीं है जिसे बनाए रखने की आवश्यकता है - क्योंकि यह तुरंत पढ़ने योग्य कोड नहीं है (इसलिए पहली जगह में प्रश्न)।

कोड सुपाठ्य होना चाहिए - अन्यथा आप भविष्य के लिए एक समय ऋण विरासत छोड़ देते हैं - क्योंकि यह उस चीज को समझने में समय लेता है जो अनावश्यक रूप से जटिल है।


8
एक चाल की मेरी परिभाषा कुछ ऐसी है जिसे हर कोई पहली बार में नहीं समझ सकता। कुछ है जो समझ से बाहर की जरूरत है एक चाल है। भी भयानक क्योंकि! ऑपरेटर को ओवरलोड किया जा सकता है ...
रिचर्ड हेरिसन

6
@ orlandu63: सरल टाइपकास्टिंग है bool(expr): यह सही काम करता है और हर कोई पहली नजर में इरादे को समझता है। !!(expr)एक दोहरा नकार है, जो गलती से बूल में परिवर्तित हो जाता है ... यह सरल नहीं है।
एड्रियन प्लिसन

12

हाँ यह सही है और नहीं, आप कुछ याद नहीं कर रहे हैं। !!बूल में रूपांतरण है। अधिक चर्चा के लिए यह प्रश्न देखें ।


9

यह संकलक चेतावनी को साइड-स्टेप करता है। इसे इस्तेमाल करे:

int _tmain(int argc, _TCHAR* argv[])
{
    int foo = 5;
    bool bar = foo;
    bool baz = !!foo;
    return 0;
}

'बार' लाइन MSVC ++ पर "बूल 'सच' या 'गलत' (प्रदर्शन चेतावनी) के लिए एक" मजबूर मूल्य "उत्पन्न करती है, लेकिन 'बाज' लाइन ठीक से गुजरती है।


1
सबसे आम तौर पर विंडोज एपीआई में ही सामना किया जाता है जो boolप्रकार के बारे में नहीं जानता है - सब कुछ के रूप में 0या 1एक में इनकोड किया गया है int
मार्क रैनसम

4

संचालक है! अतिभारित?
यदि नहीं, तो वे शायद एक चेतावनी का उत्पादन किए बिना चर को एक बूल में बदलने के लिए ऐसा कर रहे हैं। यह निश्चित रूप से चीजों को करने का एक मानक तरीका नहीं है।


4

विरासत सी डेवलपर्स, कोई बूलियन थी, इसलिए वे अक्सर #define TRUE 1और #define FALSE 0और फिर बूलियन तुलना के लिए मनमाने ढंग से संख्यात्मक डेटा प्रकार का इस्तेमाल किया। अब जब हमारे पास है bool, तो कई संकलक चेतावनी का उत्सर्जन करेंगे जब कुछ प्रकार के असाइनमेंट और तुलना संख्यात्मक प्रकार और बूलियन प्रकार के मिश्रण का उपयोग करके किए जाते हैं। विरासत कोड के साथ काम करने पर ये दोनों प्रयोग अंततः टकरा जाएंगे।

इस समस्या को हल करने के लिए, कुछ डेवलपर्स निम्नलिखित बूलियन पहचान का उपयोग: !num_valueरिटर्न bool trueअगर num_value == 0; falseअन्यथा। अगर !!num_valueलौटता bool falseहै num_value == 0; trueअन्यथा। एकल नकार को बदलने के num_valueलिए पर्याप्त है bool; हालांकि, बूलियन अभिव्यक्ति की मूल भावना को बहाल करने के लिए दोहरा निषेध आवश्यक है।

यह पैटर्न एक मुहावरे के रूप में जाना जाता है , अर्थात, आमतौर पर भाषा से परिचित लोगों द्वारा उपयोग किया जाने वाला कुछ। इसलिए, मैं इसे एक विरोधी पैटर्न के रूप में नहीं देखता, जितना मैं कर सकता हूं static_cast<bool>(num_value)। कलाकार बहुत अच्छी तरह से सही परिणाम दे सकते हैं, लेकिन कुछ संकलक फिर एक प्रदर्शन चेतावनी का उत्सर्जन करते हैं, इसलिए आपको अभी भी इसका पता लगाना होगा।

इसके समाधान के लिए अन्य तरीके से, कहने के लिए है (num_value != FALSE)। मैं उसके साथ भी ठीक हूँ, लेकिन सभी में, !!num_valueबहुत कम क्रिया है, स्पष्ट हो सकता है, और दूसरी बार जब आप इसे देख रहे हैं तो भ्रमित नहीं कर रहे हैं।


2

!! मूल सी ++ के साथ सामना करने के लिए उपयोग किया गया था जिसमें एक बूलियन प्रकार नहीं था (जैसा कि न तो सी)।


उदाहरण समस्या:

अंदर if(condition), conditionकुछ प्रकार जैसे double, int, void*, आदि का मूल्यांकन करने की आवश्यकता है , लेकिन ऐसा नहीं है boolक्योंकि यह अभी तक मौजूद नहीं है।

एक वर्ग int256(256 बिट पूर्णांक) मौजूद था और सभी पूर्णांक रूपांतरणों / जातियों को अधिभारित किया गया था।

int256 x = foo();
if (x) ...

यह जांचने के लिए कि xक्या "सत्य" या गैर-शून्य है, कुछ पूर्णांक में if (x)बदल जाएगा xऔर फिर यह आकलन करेगा कि intक्या गैर-शून्य था। का एक सामान्य अधिभार (int) xकेवल LSbit के वापस आ जाएगा xif (x)तब केवल एलएसबिट्स का परीक्षण कर रहा था x

लेकिन C ++ में !ऑपरेटर है। एक अतिभारित !xआमतौर पर सभी बिट्स का मूल्यांकन करेगा x। तो गैर-उल्टे तर्क पर वापस जाने के लिए if (!!x)उपयोग किया जाता है।

रेफरी क्या C ++ के पुराने संस्करणों ने `a (if) स्टेटमेंट में स्थिति का मूल्यांकन करते समय किसी वर्ग के` int` ऑपरेटर का उपयोग किया था?


1

जैसा कि मार्सिन ने उल्लेख किया है, यह अच्छी तरह से बात कर सकता है यदि ऑपरेटर ओवरलोडिंग खेल में है। अन्यथा, C / C ++ में यह कोई फर्क नहीं पड़ता सिवाय इसके कि आप निम्नलिखित चीजों में से एक कर रहे हैं:

  • true(या सी में TRUEमैक्रो की तरह कुछ ) की सीधी तुलना , जो लगभग हमेशा एक बुरा विचार है। उदाहरण के लिए:

    if (api.lookup("some-string") == true) {...}

  • आप बस कुछ सख्त 0/1 मान में परिवर्तित करना चाहते हैं। C ++ में boolवसीयत का एक असाइनमेंट इस तरह से होगा (उन चीजों के लिए जो अनुमानित रूप से परिवर्तनीय हैं bool)। सी में या यदि आप एक गैर-बूल चर के साथ काम कर रहे हैं, तो यह एक मुहावरा है जिसे मैंने देखा है, लेकिन मैं (some_variable != 0)खुद को विविधता पसंद करता हूं।

मुझे लगता है कि यह एक बड़ी बूलियन अभिव्यक्ति के संदर्भ में बस चीजों को जकड़ लेता है।


1

यदि चर वस्तु प्रकार का है, तो यह हो सकता है! ऑपरेटर को परिभाषित किया गया है, लेकिन बूल को कोई कास्ट नहीं किया गया है (या अलग-अलग शब्दार्थों के साथ इंट करने के लिए एक अंतर्निहित कास्ट बदतर है। कॉलिंग ऑपरेटर को दो बार परिणामस्वरूप बूल में बदल देता है जो अजीब मामलों में भी काम करता है।


0

यह सही है लेकिन, C में, यहाँ व्यर्थ - 'if' और '&&' बिना 'के' के समान ही अभिव्यक्ति का व्यवहार करेगा।

C ++ में ऐसा करने का कारण, मुझे लगता है, कि '&&' ओवरलोड हो सकता है। लेकिन फिर, इसलिए यह '!' हो सकता है, इसलिए यह वास्तव में गारंटी नहीं देता है कि आपको एक बूल मिलता है, variableऔर प्रकारों के लिए कोड को देखे बिना api.call। शायद अधिक C ++ अनुभव वाला कोई व्यक्ति व्याख्या कर सकता है; शायद इसका मतलब रक्षा-में-गहराई की तरह माप है, गारंटी नहीं।


कंपाइलर मानों को उसी तरह से व्यवहार करेगा, यदि इसका उपयोग केवल एक ऑपरेंड के रूप में किया जाता है ifया &&, लेकिन !!कुछ कंपाइलरों पर मदद मिल सकती है अगर इसके if (!!(number & mask))साथ प्रतिस्थापित किया जाता है bit triggered = !!(number & mask); if (triggered); कुछ प्रकार के साथ कुछ एम्बेडेड संकलक पर, उदाहरण के लिए 256 से थोड़ा प्रकार असाइन करना शून्य का उत्पादन करेगा। !!जाहिरा तौर पर सुरक्षित परिवर्तन के बिना ( ifएक चर और फिर शाखा में हालत की नकल ) सुरक्षित नहीं होगा।
सुपरकाट

0

शायद प्रोग्रामर कुछ इस तरह से सोच रहे थे ...

!! myAnswer बूलियन है। संदर्भ में, यह बूलियन बन जाना चाहिए, लेकिन मुझे सिर्फ यह सुनिश्चित करने के लिए बैंग बैंग्स से प्यार है, क्योंकि एक समय में एक रहस्यमय बग था जो मुझे थोड़ा सा था, और बैंग बैंग, मैंने इसे मार दिया।


0

यह डबल-बैंग ट्रिक का एक उदाहरण हो सकता है , अधिक जानकारी के लिए सुरक्षित बूल मुहावर देखें। यहां मैं लेख के पहले पृष्ठ को संक्षेप में प्रस्तुत करता हूं।

सी ++ में कक्षाओं के लिए बूलियन परीक्षण प्रदान करने के कई तरीके हैं।

एक स्पष्ट तरीका operator boolरूपांतरण ऑपरेटर है।

// operator bool version
  class Testable {
    bool ok_;
  public:
    explicit Testable(bool b=true):ok_(b) {}

    operator bool() const { // use bool conversion operator
      return ok_;
    }
  };

हम कक्षा का परीक्षण कर सकते हैं,

Testable test;
  if (test) 
    std::cout << "Yes, test is working!\n";
  else 
    std::cout << "No, test is not working!\n";

हालांकि, opereator boolअसुरक्षित माना जाता है क्योंकि यह निरर्थक संचालन की अनुमति देता है जैसे test << 1;या int i=test

उपयोग करना अधिक operator!सुरक्षित है क्योंकि हम अंतर्निहित रूपांतरण या अतिभारित मुद्दों से बचते हैं।

कार्यान्वयन तुच्छ है,

bool operator!() const { // use operator!
    return !ok_;
  }

Testableवस्तु का परीक्षण करने के दो मुहावरे हैं

  Testable test;
  if (!!test) 
    std::cout << "Yes, test is working!\n";
  if (!test2) {
    std::cout << "No, test2 is not working!\n";

पहला संस्करण if (!!test)वह है जिसे कुछ लोग डबल-बैंग ट्रिक कहते हैं


1
C ++ 11 के बाद से, explicit operator boolअन्य अभिन्न प्रकारों में निहित रूपांतरण को रोकने के लिए उपयोग किया जा सकता है।
अर्ने वोगेल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.