कोडिंग शैली अंततः व्यक्तिपरक है, और यह अत्यधिक संभावना नहीं है कि इससे पर्याप्त प्रदर्शन लाभ होगा। लेकिन यहाँ मैं क्या कहूँगा कि आप एकसमान इनिशियलाइज़ेशन के उदार उपयोग से लाभान्वित होंगे:
अतिरेक टाइपनामों को कम करता है
निम्नलिखित को धयान मे रखते हुए:
vec3 GetValue()
{
return vec3(x, y, z);
}
मुझे vec3
दो बार टाइप करने की आवश्यकता क्यों है ? क्या इसका कोई मतलब है? कंपाइलर अच्छी तरह से जानता है कि फ़ंक्शन क्या देता है। मैं बस यह क्यों नहीं कह सकता, "जो मैं इन मूल्यों के साथ लौटता हूं, उसे वापस लौटाने के निर्माता को बुलाऊं?" यूनिफ़ॉर्म इनिशियलाइज़ेशन के साथ, मैं कर सकता हूँ:
vec3 GetValue()
{
return {x, y, z};
}
सब कुछ काम करता है।
फ़ंक्शन तर्कों के लिए भी बेहतर है। इस पर विचार करो:
void DoSomething(const std::string &str);
DoSomething("A string.");
यह टाइप किएनाम टाइप किए बिना काम करता है, क्योंकि std::string
एक const char*
अंतर्निहित रूप से खुद का निर्माण करना जानता है । एक दम बढ़िया। लेकिन क्या होगा अगर वह स्ट्रिंग से आया, रैपिडएक्सएमएल कहो। या एक लुआ स्ट्रिंग। यही है, चलो कहते हैं कि मैं वास्तव में सामने की स्ट्रिंग की लंबाई जानता हूं। std::string
निर्माता है कि एक लेता है const char*
स्ट्रिंग की लंबाई लेने के लिए करता है, तो मैं सिर्फ एक पारित करना होगा const char*
।
एक अधिभार है जो स्पष्ट रूप से एक लंबाई लेता है। लेकिन इसका उपयोग करने के लिए, मुझे यह करना होगा DoSomething(std::string(strValue, strLen))
:। क्यों वहाँ में अतिरिक्त टाइपनेम है? कंपाइलर जानता है कि प्रकार क्या है। बस के साथ की तरह auto
, हम अतिरिक्त टाइपनेम होने से बच सकते हैं:
DoSomething({strValue, strLen});
यह सिर्फ काम करता है। कोई टाइपनेम, कोई उपद्रव, कुछ नहीं। संकलक अपना काम करता है, कोड छोटा होता है, और हर कोई खुश होता है।
दी गई, यह तर्क दिया जाना है कि पहला संस्करण ( DoSomething(std::string(strValue, strLen))
) अधिक सुपाठ्य है। यही है, यह स्पष्ट है कि क्या चल रहा है और कौन क्या कर रहा है। यह सच है, एक हद तक; यूनिफॉर्म इनिशियलाइज़ेशन-आधारित कोड को समझने के लिए फ़ंक्शन प्रोटोटाइप को देखने की आवश्यकता होती है। यही कारण है कि कुछ का कहना है कि आपको गैर-कॉन्स्टेंस संदर्भ द्वारा कभी भी मापदंडों को पास नहीं करना चाहिए: ताकि आप कॉल साइट पर देख सकें कि क्या कोई मूल्य संशोधित किया जा रहा है।
लेकिन उसी के लिए कहा जा सकता है auto
; यह जानने से कि आपको किसकी auto v = GetSomething();
परिभाषा देखने की आवश्यकता है GetSomething
। लेकिन जब तक आप इसके पास नहीं auto
पहुंचते हैं, तब तक इसे लापरवाह परित्याग के पास इस्तेमाल करने से नहीं रोका जाता है। व्यक्तिगत रूप से, मुझे लगता है कि जब आप इसकी आदत डाल लेंगे तो यह ठीक हो जाएगा। खासतौर पर एक अच्छी IDE के साथ।
मोस्ट वैक्सिंग पार्स कभी नहीं
यहाँ कुछ कोड है।
class Bar;
void Func()
{
int foo(Bar());
}
पॉप क्विज: क्या है foo
? यदि आपने "एक चर" उत्तर दिया है, तो आप गलत हैं। यह वास्तव में एक फ़ंक्शन का प्रोटोटाइप है जो अपने पैरामीटर को एक फ़ंक्शन के रूप में लेता है जो एक रिटर्न देता है Bar
, और foo
फ़ंक्शन का रिटर्न वैल्यू एक इंट है।
इसे C ++ का "Most Vexing Parse" कहा जाता है क्योंकि यह मनुष्य के लिए बिल्कुल कोई मतलब नहीं रखता है। लेकिन सी ++ के नियमों को दुख की आवश्यकता है: यदि इसे संभवतः फ़ंक्शन प्रोटोटाइप के रूप में व्याख्या किया जा सकता है, तो यह होगा । समस्या यह है Bar()
; यह दो चीजों में से एक हो सकता है। यह एक प्रकार का नाम हो सकता है Bar
, जिसका अर्थ है कि यह एक अस्थायी निर्माण कर रहा है। या यह एक ऐसा कार्य हो सकता है जो कोई पैरामीटर नहीं लेता है और एक रिटर्न देता है Bar
।
समान आरंभीकरण को फ़ंक्शन प्रोटोटाइप के रूप में व्याख्या नहीं किया जा सकता है:
class Bar;
void Func()
{
int foo{Bar{}};
}
Bar{}
हमेशा एक अस्थायी बनाता है। int foo{...}
हमेशा एक चर बनाता है।
ऐसे कई मामले हैं जहां आप उपयोग करना चाहते हैं, Typename()
लेकिन केवल C ++ के पार्सिंग नियमों के कारण नहीं कर सकते। के साथ Typename{}
, कोई अस्पष्टता नहीं है।
कारण नहीं
आपके द्वारा दी जाने वाली एकमात्र वास्तविक शक्ति संकीर्ण है। आप यूनिफ़ॉर्म इनिशियलाइज़ेशन के साथ एक बड़े मूल्य के साथ एक छोटे मूल्य को इनिशियलाइज़ नहीं कर सकते हैं।
int val{5.2};
वह संकलन नहीं करेगा। आप पुराने जमाने की इनिशियलाइज़ेशन के साथ कर सकते हैं, लेकिन यूनिफ़ॉर्म इनिशियलाइज़ेशन नहीं।
यह वास्तव में काम करने के लिए शुरुआती सूची बनाने के लिए किया गया था। अन्यथा, शुरुआती सूची के प्रकारों के संबंध में बहुत सारे अस्पष्ट मामले होंगे।
बेशक, कुछ तर्क दे सकते हैं कि इस तरह के कोड संकलन के योग्य नहीं हैं। मैं व्यक्तिगत रूप से सहमत होने के लिए होता हूं; संकीर्णता बहुत खतरनाक है और इससे अप्रिय व्यवहार हो सकता है। कंपाइलर स्टेज पर उन समस्याओं को जल्दी पकड़ना शायद सबसे अच्छा है। बहुत कम से कम, संकीर्णता से पता चलता है कि कोई व्यक्ति कोड के बारे में बहुत कठिन नहीं सोच रहा है।
ध्यान दें कि कंपाइलर्स आम तौर पर आपको इस तरह की चीज़ों के बारे में चेतावनी देते हैं अगर आपका चेतावनी स्तर अधिक है। तो वास्तव में, यह सब चेतावनी को एक लागू त्रुटि में बदल देता है। कुछ लोग कह सकते हैं कि आपको ऐसा करना चाहिए;)
ऐसा नहीं करने के लिए एक और कारण है:
std::vector<int> v{100};
यह क्या करता है? यह vector<int>
एक सौ डिफ़ॉल्ट निर्मित वस्तुओं के साथ बना सकता है । या यह vector<int>
1 आइटम के साथ बना सकता है, जिसका मूल्य है 100
। दोनों सैद्धांतिक रूप से संभव हैं।
वास्तविकता में, यह उत्तरार्द्ध करता है।
क्यों? प्रारंभिक सूचियाँ समान रूप से समान सिंटैक्स का उपयोग करती हैं। तो यह बताने के लिए कुछ नियम हैं कि अस्पष्टता के मामले में क्या करना है। नियम बहुत सरल है: यदि कंपाइलर इनिशियल लिस्ट कंस्ट्रक्टर का ब्रेस-इनिशियलाइज़ लिस्ट के साथ उपयोग कर सकता है , तो यह होगा । चूंकि vector<int>
एक प्रारंभिक सूची निर्माणकर्ता को लगती है initializer_list<int>
, और {100} वैध हो सकता है initializer_list<int>
, इसलिए यह होना चाहिए ।
आकार देने वाले निर्माणकर्ता को प्राप्त करने के लिए, आपको ()
इसके बजाय उपयोग करना होगा {}
।
ध्यान दें कि अगर यह vector
एक पूर्णांक के लिए परिवर्तनीय नहीं था, तो ऐसा कुछ नहीं होगा। एक initializer_list उस vector
प्रकार के initializer सूची निर्माता को फिट नहीं करेगा , और इसलिए कंपाइलर अन्य कंस्ट्रक्टरों से लेने के लिए स्वतंत्र होगा।