कॉन्स्ट्रेक्स बनाम मैक्रोज़


95

मुझे मैक्रोज़ का उपयोग करना कहां पसंद करना चाहिए और मुझे कॉन्स्ट्रेक्स कहां पसंद करना चाहिए ? वे मूल रूप से एक ही नहीं हैं?

#define MAX_HEIGHT 720

बनाम

constexpr unsigned int max_height = 720;

4
AFAIK कॉन्स्ट्रेक्स अधिक प्रकार की सुरक्षा प्रदान करता है
कोड-अपरेंटिस

15
आसान: constexr, हमेशा।
एन। 'सर्वनाम' मी।

अपने कुछ सवालों के जवाब
ढेर कर सकते हैं

जवाबों:


151

वे मूल रूप से एक ही नहीं हैं?

नहीं, बिल्कुल नहीं। आस - पास भी नहीं।

इस सच के बावजूद से अपने मैक्रो एक है intऔर अपने constexpr unsignedएक है unsignedवहाँ महत्वपूर्ण अंतर और मैक्रो केवल कर रहे हैं, एक लाभ।

क्षेत्र

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

#define MAX_HEIGHT 720
constexpr int max_height = 720;

class Window {
  // ...
  int max_height;
};

यह एक सदस्य चर कहा जाता है max_heightक्योंकि यह एक वर्ग के सदस्य है और इसलिए एक अलग गुंजाइश है ठीक है, और नाम स्थान पर एक से अलग है। यदि आपने MAX_HEIGHTसदस्य के लिए नाम का पुन: उपयोग करने का प्रयास किया है, तो प्रीप्रोसेसर इसे इस बकवास में बदल देगा जो संकलन नहीं करेगा:

class Window {
  // ...
  int 720;
};

यही कारण है कि आपको मैक्रो UGLY_SHOUTY_NAMESको यह सुनिश्चित करने के लिए देना होगा कि वे बाहर खड़े रहें और आप झड़पों से बचने के लिए उनका नामकरण करने में सावधानी बरतें। यदि आप अनावश्यक रूप से मैक्रोज़ का उपयोग नहीं करते हैं, तो आपको उस बारे में चिंता करने की ज़रूरत नहीं है (और पढ़ना नहीं है SHOUTY_NAMES)।

यदि आप किसी फ़ंक्शन के अंदर केवल एक स्थिरांक चाहते हैं, तो आप एक मैक्रो के साथ ऐसा नहीं कर सकते, क्योंकि प्रीप्रोसेसर को पता नहीं है कि फ़ंक्शन क्या है या इसके अंदर होने का क्या मतलब है। किसी मैक्रो को केवल उस फ़ाइल के एक निश्चित हिस्से तक सीमित करने के लिए जिसे आपको #undefफिर से इसकी आवश्यकता है :

int limit(int height) {
#define MAX_HEIGHT 720
  return std::max(height, MAX_HEIGHT);
#undef MAX_HEIGHT
}

कहीं अधिक समझदार की तुलना करें:

int limit(int height) {
  constexpr int max_height = 720;
  return std::max(height, max_height);
}

आप मैक्रो को क्यों पसंद करेंगे?

एक वास्तविक स्मृति स्थान

एक कॉन्स्टैक्स वेरिएबल एक वैरिएबल है इसलिए यह वास्तव में प्रोग्राम में मौजूद है और आप सामान्य सी ++ चीजें कर सकते हैं जैसे कि इसका पता लें और इसके संदर्भ को बांधें।

इस कोड में अपरिभाषित व्यवहार है:

#define MAX_HEIGHT 720
int limit(int height) {
  const int& h = std::max(height, MAX_HEIGHT);
  // ...
  return h;
}

समस्या यह है कि MAX_HEIGHTएक चर नहीं है, इसलिए std::maxएक कॉल के लिए एक अस्थायी intसंकलक द्वारा बनाया जाना चाहिए। फिर जो संदर्भ दिया जाता है std::max, वह उस अस्थायी को संदर्भित कर सकता है, जो उस बयान के अंत के बाद मौजूद नहीं है, इसलिए return hअमान्य मेमोरी तक पहुँच प्राप्त करता है।

यह समस्या बस एक उचित चर के साथ मौजूद नहीं है, क्योंकि इसकी स्मृति में एक निश्चित स्थान है जो दूर नहीं जाता है:

int limit(int height) {
  constexpr int max_height = 720;
  const int& h = std::max(height, max_height);
  // ...
  return h;
}

(व्यवहार में आप शायद घोषित int hनहीं करेंगे, const int& hलेकिन समस्या अधिक सूक्ष्म संदर्भों में उत्पन्न हो सकती है।)

प्रीप्रोसेसर शर्तें

मैक्रो को पसंद करने का एकमात्र समय तब होता है जब आपको इसके मूल्य को पूर्वप्रक्रमक द्वारा समझने की आवश्यकता #ifहोती है, जैसे कि स्थितियों में उपयोग के लिए

#define MAX_HEIGHT 720
#if MAX_HEIGHT < 256
using height_type = unsigned char;
#else
using height_type = unsigned int;
#endif

आप यहाँ एक वैरिएबल का उपयोग नहीं कर सकते, क्योंकि प्रीप्रोसेसर को समझ में नहीं आता है कि नाम से चर कैसे देखें। यह केवल शुरुआत के साथ मैक्रो विस्तार और निर्देशों जैसी बुनियादी बहुत बुनियादी बातें समझता है #(जैसे #includeऔर #defineऔर #if)।

यदि आप एक स्थिरांक चाहते हैं जिसे प्रीप्रोसेसर द्वारा समझा जा सकता है तो आपको इसे परिभाषित करने के लिए प्रीप्रोसेसर का उपयोग करना चाहिए। यदि आप सामान्य C ++ कोड के लिए स्थिरांक चाहते हैं, तो सामान्य C ++ कोड का उपयोग करें।

उपरोक्त उदाहरण सिर्फ एक प्रीप्रोसेसर स्थिति को प्रदर्शित करने के लिए है, लेकिन यह भी कि कोड प्रीप्रोसेसर का उपयोग करने से बच सकता है:

using height_type = std::conditional_t<max_height < 256, unsigned char, unsigned int>;

3
एक constexprचर को तब तक मेमोरी पर कब्जा करने की ज़रूरत नहीं है जब तक कि उसका पता (एक पॉइंटर / संदर्भ) नहीं लिया जाता है; अन्यथा, इसे पूरी तरह से अनुकूलित किया जा सकता है (और मुझे लगता है कि मानक हो सकता है जो इसकी गारंटी देता है)। मैं इस पर जोर देना चाहता हूं ताकि लोग enumगुमराह किए गए विचार से पुराने, हीन ' हैक' का उपयोग जारी न रखें जो कि एक तुच्छ constexprकि भंडारण की आवश्यकता नहीं है फिर भी कुछ पर कब्जा कर लेंगे।
अंडरस्कोर_ड

3
आपका "एक वास्तविक मेमोरी लोकेशन" सेक्शन गलत है: 1. आप वैल्यू (इंट) द्वारा लौट रहे हैं, इसलिए एक कॉपी बनाई गई है, अस्थायी कोई समस्या नहीं है। 2. यदि आप संदर्भ (इंट &) द्वारा वापस आ गए थे, तो आपका int heightमैक्रो के रूप में एक समस्या के रूप में होगा, क्योंकि इसका दायरा फ़ंक्शन से बंधा हुआ है, अनिवार्य रूप से अस्थायी भी। 3. उपरोक्त टिप्पणी, "कांस्ट इंट एंड एच अस्थायी के जीवनकाल का विस्तार करेगा" सही है।
PoweredByRice

4
@underscore_d सच है, लेकिन यह तर्क नहीं बदलता है। वेरिएबल को तब तक स्टोरेज की आवश्यकता नहीं होगी, जब तक कि इसमें ओड-यूज़ न हो। मुद्दा यह है कि जब भंडारण के साथ एक वास्तविक चर की आवश्यकता होती है, तो कॉन्स्ट्रेप चर सही काम करता है।
जोनाथन वेकली

1
@PoweredByRice 1. समस्या का रिटर्न वैल्यू से कोई लेना-देना नहीं है limit, समस्या का रिटर्न वैल्यू है std::max। 2. हाँ, इसीलिए यह एक संदर्भ वापस नहीं करता है। 3. गलत, ऊपर कोलीरू लिंक देखें।
जोनाथन

3
@PoweredByRice आह, आप वास्तव में समझाने की जरूरत नहीं है कि C ++ मेरे लिए कैसे काम करता है। यदि आपके पास const int& h = max(x, y);और maxमूल्य अपनी वापसी मान के जीवनकाल से रिटर्न बढ़ा दिया गया है। रिटर्न प्रकार से नहीं, बल्कि const int&इसके लिए बाध्य है। मैंने जो लिखा है वह सही है।
जोनाथन वाकेली

11

सामान्यतया, आपको constexprजब भी हो सकता है, और मैक्रोज़ का उपयोग करना चाहिए यदि कोई अन्य समाधान संभव नहीं है।

दलील:

मैक्रोज़ कोड में एक सरल प्रतिस्थापन है, और इस कारण से, वे अक्सर संघर्ष उत्पन्न करते हैं (उदाहरण के लिए windows.h maxमैक्रो बनाम std::max)। इसके अतिरिक्त, एक मैक्रो जो काम करता है आसानी से एक अलग तरीके से उपयोग किया जा सकता है जो तब अजीब संकलन त्रुटियों को ट्रिगर कर सकता है। (जैसे Q_PROPERTYसंरचना सदस्यों पर प्रयोग किया जाता है)

उन सभी अनिश्चितताओं के कारण, मैक्रोज़ से बचने के लिए यह अच्छा कोड स्टाइल है, ठीक उसी तरह जैसे आप आमतौर पर गोटो से बचते हैं।

constexpr शब्दार्थ परिभाषित किया गया है, और इस प्रकार आम तौर पर बहुत कम मुद्दों को उत्पन्न करता है।


1
किस मामले में एक मैक्रो अपरिहार्य का उपयोग कर रहा है?
टॉम डोरोन

3
सशर्त संकलन #ifअर्थात उन चीजों का उपयोग करना जो प्रीप्रोसेसर वास्तव में उपयोगी है। स्थिरांक को परिभाषित करना उन चीजों में से एक नहीं है जो प्रीप्रोसेसर के लिए उपयोगी है, जब तक कि स्थिरांक मैक्रो नहीं होना चाहिए क्योंकि इसका उपयोग प्रीप्रोसेसर स्थितियों में किया जाता है #if। यदि स्थिरांक सामान्य C ++ कोड में उपयोग के लिए है (प्रीप्रोसेसर निर्देश नहीं) तो सामान्य C ++ वैरिएबल का उपयोग करें, न कि प्रीप्रोसेसर मैक्रो।
जोनाथन वेकली

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

मैं कहूंगा कि संकलक स्विच न तो एक अच्छा विचार है। हालांकि, मैं पूरी तरह से समझता हूं कि इसे कुछ समय (मैक्रोज़) की भी आवश्यकता है, विशेष रूप से क्रॉस-प्लेटफॉर्म या एम्बेडेड कोड से निपटने की। आपके प्रश्न का उत्तर देने के लिए: यदि आप पहले से ही प्रीप्रोसेसर के साथ काम कर रहे हैं, तो मैं मैक्रो का उपयोग स्पष्ट और सहज रखने के लिए करूँगा कि प्रीप्रोसेसर क्या है और संकलन समय क्या है। मैं भी जोरदार टिप्पणी करने और इसे संभव के रूप में संक्षिप्त और स्थानीय रूप में उपयोग करने का सुझाव दूंगा (मैक्रों के चारों ओर फैलने से बचें या # 100 पंक्तियों #)। हो सकता है कि अपवाद विशिष्ट #ifndef guard (मानक #pragma के लिए एक बार) है जो अच्छी तरह से समझा जाता है।
एड्रियन मैयर

3

जोनाथन वेकली का शानदार जवाब । मैं आपको सलाह भी दूंगा कि जोगोजापान के जवाब पर एक नज़र डालें कि अंतर क्या है constऔर constexprइससे पहले कि आप मैक्रोज़ के उपयोग पर भी विचार करें।

मैक्रों गूंगे हैं, लेकिन एक अच्छे तरीके से। मूल रूप से आजकल वे एक निर्माण-सहायता करते हैं जब आप चाहते हैं कि आपके कोड के बहुत विशिष्ट हिस्से केवल कुछ बिल्ड पैरामीटरों की उपस्थिति में संकलित किए जाएं जो "परिभाषित" हो रहे हैं। आमतौर पर, यह सब साधन बेहतर अभी तक अपने मैक्रो का नाम, या ले जा रहा है, के लिए यह एक कॉल Trigger, और जोड़ने बातें, पसंद /D:Trigger, -DTriggerनिर्माण उपकरणों के लिए, आदि के लिए इस्तेमाल किया जा रहा है।

जबकि मैक्रोज़ के लिए कई अलग-अलग उपयोग हैं, ये दो हैं जिन्हें मैं सबसे अधिक बार देखता हूं जो खराब / आउट-डेटेड अभ्यास नहीं हैं:

  1. हार्डवेयर और प्लेटफ़ॉर्म-विशिष्ट कोड अनुभाग
  2. वर्बोसिटी बढ़ जाती है

इसलिए जब आप ओपी के मामले में इंट के साथ constexprया एक को परिभाषित करने के एक ही लक्ष्य को पूरा कर सकते हैं MACRO, तो आधुनिक सम्मेलनों का उपयोग करते समय दोनों के ओवरलैप होने की संभावना नहीं है। यहां कुछ सामान्य मैक्रो-उपयोग हैं जिन्हें अभी तक चरणबद्ध नहीं किया गया है।

#if defined VERBOSE || defined DEBUG || defined MSG_ALL
    // Verbose message-handling code here
#endif

मैक्रो-उपयोग के लिए एक और उदाहरण के रूप में, मान लें कि आपके पास जारी करने के लिए कुछ आगामी हार्डवेयर हैं, या शायद इसकी एक विशिष्ट पीढ़ी है जिसमें कुछ पेचीदा कार्य हैं जो दूसरों की आवश्यकता नहीं है। हम इस मैक्रो को इस तरह परिभाषित करेंगे GEN_3_HW

#if defined GEN_3_HW && defined _WIN64
    // Windows-only special handling for 64-bit upcoming hardware
#elif defined GEN_3_HW && defined __APPLE__
    // Special handling for macs on the new hardware
#elif !defined _WIN32 && !defined __linux__ && !defined __APPLE__ && !defined __ANDROID__ && !defined __unix__ && !defined __arm__
    // Greetings, Outlander! ;)
#else
    // Generic handling
#endif
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.