मुझे मैक्रोज़ का उपयोग करना कहां पसंद करना चाहिए और मुझे कॉन्स्ट्रेक्स कहां पसंद करना चाहिए ? वे मूल रूप से एक ही नहीं हैं?
#define MAX_HEIGHT 720
बनाम
constexpr unsigned int max_height = 720;
मुझे मैक्रोज़ का उपयोग करना कहां पसंद करना चाहिए और मुझे कॉन्स्ट्रेक्स कहां पसंद करना चाहिए ? वे मूल रूप से एक ही नहीं हैं?
#define MAX_HEIGHT 720
बनाम
constexpr unsigned int max_height = 720;
जवाबों:
वे मूल रूप से एक ही नहीं हैं?
नहीं, बिल्कुल नहीं। आस - पास भी नहीं।
इस सच के बावजूद से अपने मैक्रो एक है 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>;
constexpr
चर को तब तक मेमोरी पर कब्जा करने की ज़रूरत नहीं है जब तक कि उसका पता (एक पॉइंटर / संदर्भ) नहीं लिया जाता है; अन्यथा, इसे पूरी तरह से अनुकूलित किया जा सकता है (और मुझे लगता है कि मानक हो सकता है जो इसकी गारंटी देता है)। मैं इस पर जोर देना चाहता हूं ताकि लोग enum
गुमराह किए गए विचार से पुराने, हीन ' हैक' का उपयोग जारी न रखें जो कि एक तुच्छ constexpr
कि भंडारण की आवश्यकता नहीं है फिर भी कुछ पर कब्जा कर लेंगे।
int height
मैक्रो के रूप में एक समस्या के रूप में होगा, क्योंकि इसका दायरा फ़ंक्शन से बंधा हुआ है, अनिवार्य रूप से अस्थायी भी। 3. उपरोक्त टिप्पणी, "कांस्ट इंट एंड एच अस्थायी के जीवनकाल का विस्तार करेगा" सही है।
limit
, समस्या का रिटर्न वैल्यू है std::max
। 2. हाँ, इसीलिए यह एक संदर्भ वापस नहीं करता है। 3. गलत, ऊपर कोलीरू लिंक देखें।
const int& h = max(x, y);
और max
मूल्य अपनी वापसी मान के जीवनकाल से रिटर्न बढ़ा दिया गया है। रिटर्न प्रकार से नहीं, बल्कि const int&
इसके लिए बाध्य है। मैंने जो लिखा है वह सही है।
सामान्यतया, आपको constexpr
जब भी हो सकता है, और मैक्रोज़ का उपयोग करना चाहिए यदि कोई अन्य समाधान संभव नहीं है।
मैक्रोज़ कोड में एक सरल प्रतिस्थापन है, और इस कारण से, वे अक्सर संघर्ष उत्पन्न करते हैं (उदाहरण के लिए windows.h max
मैक्रो बनाम std::max
)। इसके अतिरिक्त, एक मैक्रो जो काम करता है आसानी से एक अलग तरीके से उपयोग किया जा सकता है जो तब अजीब संकलन त्रुटियों को ट्रिगर कर सकता है। (जैसे Q_PROPERTY
संरचना सदस्यों पर प्रयोग किया जाता है)
उन सभी अनिश्चितताओं के कारण, मैक्रोज़ से बचने के लिए यह अच्छा कोड स्टाइल है, ठीक उसी तरह जैसे आप आमतौर पर गोटो से बचते हैं।
constexpr
शब्दार्थ परिभाषित किया गया है, और इस प्रकार आम तौर पर बहुत कम मुद्दों को उत्पन्न करता है।
#if
अर्थात उन चीजों का उपयोग करना जो प्रीप्रोसेसर वास्तव में उपयोगी है। स्थिरांक को परिभाषित करना उन चीजों में से एक नहीं है जो प्रीप्रोसेसर के लिए उपयोगी है, जब तक कि स्थिरांक मैक्रो नहीं होना चाहिए क्योंकि इसका उपयोग प्रीप्रोसेसर स्थितियों में किया जाता है #if
। यदि स्थिरांक सामान्य C ++ कोड में उपयोग के लिए है (प्रीप्रोसेसर निर्देश नहीं) तो सामान्य C ++ वैरिएबल का उपयोग करें, न कि प्रीप्रोसेसर मैक्रो।
जोनाथन वेकली का शानदार जवाब । मैं आपको सलाह भी दूंगा कि जोगोजापान के जवाब पर एक नज़र डालें कि अंतर क्या है const
और constexpr
इससे पहले कि आप मैक्रोज़ के उपयोग पर भी विचार करें।
मैक्रों गूंगे हैं, लेकिन एक अच्छे तरीके से। मूल रूप से आजकल वे एक निर्माण-सहायता करते हैं जब आप चाहते हैं कि आपके कोड के बहुत विशिष्ट हिस्से केवल कुछ बिल्ड पैरामीटरों की उपस्थिति में संकलित किए जाएं जो "परिभाषित" हो रहे हैं। आमतौर पर, यह सब साधन बेहतर अभी तक अपने मैक्रो का नाम, या ले जा रहा है, के लिए यह एक कॉल Trigger
, और जोड़ने बातें, पसंद /D:Trigger
, -DTrigger
निर्माण उपकरणों के लिए, आदि के लिए इस्तेमाल किया जा रहा है।
जबकि मैक्रोज़ के लिए कई अलग-अलग उपयोग हैं, ये दो हैं जिन्हें मैं सबसे अधिक बार देखता हूं जो खराब / आउट-डेटेड अभ्यास नहीं हैं:
इसलिए जब आप ओपी के मामले में इंट के साथ 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