ब्रेस-एनक्लोजर इनिशियलाइज़र का उपयोग कब करें?


94

C ++ 11 में, हमारे पास वर्गों को इनिशियलाइज़ करने के लिए नया सिंटैक्स है जो हमें बड़ी संख्या में संभावनाएँ देता है कि कैसे वेरिएबल को इनिशियलाइज़ किया जाए।

{ // Example 1
  int b(1);
  int a{1};
  int c = 1;
  int d = {1};
}
{ // Example 2
  std::complex<double> b(3,4);
  std::complex<double> a{3,4};
  std::complex<double> c = {3,4};
  auto d = std::complex<double>(3,4);
  auto e = std::complex<double>{3,4};
}
{ // Example 3
  std::string a(3,'x');
  std::string b{3,'x'}; // oops
}
{ // Example 4
  std::function<int(int,int)> a(std::plus<int>());
  std::function<int(int,int)> b{std::plus<int>()};
}
{ // Example 5
  std::unique_ptr<int> a(new int(5));
  std::unique_ptr<int> b{new int(5)};
}
{ // Example 6
  std::locale::global(std::locale("")); // copied from 22.4.8.3
  std::locale::global(std::locale{""});
}
{ // Example 7
  std::default_random_engine a {}; // Stroustrup's FAQ
  std::default_random_engine b;
}
{ // Example 8
  duration<long> a = 5; // Stroustrup's FAQ too
  duration<long> b(5);
  duration<long> c {5};
}

मेरे द्वारा घोषित किए गए प्रत्येक चर के लिए, मुझे सोचना होगा कि मुझे किस सिंटैक्स का उपयोग करना चाहिए और इससे मेरी कोडिंग गति धीमी हो जाती है। मुझे यकीन है कि घुंघराले कोष्ठक शुरू करने का इरादा नहीं था।

जब टेम्प्लेट कोड की बात आती है, तो सिंटैक्स को बदलने से विभिन्न अर्थ हो सकते हैं, इसलिए सही तरीके से जाना आवश्यक है।

मुझे आश्चर्य है कि क्या एक सार्वभौमिक दिशानिर्देश है जिसे सिंटैक्स को चुना जाना चाहिए।


1
{} आरंभीकरण से अपरिचित व्यवहार का एक उदाहरण: स्ट्रिंग (50, 'x') बनाम स्ट्रिंग {50, 'x'} यहां
P i

जवाबों:


64

मुझे लगता है कि निम्नलिखित एक अच्छा दिशानिर्देश हो सकता है:

  • यदि आप जिस एकल (एकल) मूल्य के साथ तुलना कर रहे हैं , उसका उद्देश्य वस्तु का सटीक मूल्य होना है , तो कॉपी ( =) आरंभीकरण का उपयोग करें (क्योंकि तब त्रुटि के मामले में, आप कभी भी गलती से एक स्पष्ट निर्माणकर्ता को आमंत्रित नहीं करेंगे, जो आम तौर पर प्रदान किए गए मूल्य की व्याख्या करता है अलग ढंग से)। उन स्थानों पर जहाँ प्रतिलिपि आरंभीकरण उपलब्ध नहीं है, देखें कि क्या ब्रेस आरंभीकरण के सही शब्दार्थ हैं, और यदि हां, तो उसका उपयोग करें; अन्यथा कोष्ठक आरंभीकरण का उपयोग करें (यदि वह भी उपलब्ध नहीं है, तो आप वैसे भी भाग्य से बाहर हैं)।

  • यदि आप जिन मानों के साथ आरंभ कर रहे हैं , वे ऑब्जेक्ट में संग्रहीत मानों की एक सूची है (जैसे वेक्टर / सरणी के तत्व, या किसी जटिल संख्या के वास्तविक / काल्पनिक भाग), तो यदि उपलब्ध हो तो घुंघराले ब्रेस का उपयोग करें।

  • यदि आप जिन मानों के साथ आरंभ कर रहे हैं वे संग्रहीत किए जाने वाले मान नहीं हैं, लेकिन वर्णन करते हैं ऑब्जेक्ट के इच्छित मान / स्थिति का करते हैं, कोष्ठक का उपयोग करें। उदाहरण a vectorया फ़ाइल नाम तर्क के आकार तर्क हैं fstream


4
@ user1304032: एक लोकेल एक स्ट्रिंग नहीं है, इसलिए आप प्रतिलिपि आरंभीकरण का उपयोग नहीं करेंगे। एक लोकेल में एक स्ट्रिंग भी नहीं है (यह उस स्ट्रिंग को कार्यान्वयन विवरण के रूप में संग्रहीत कर सकता है, लेकिन यह इसका उद्देश्य नहीं है), इसलिए आप ब्रेस इनिशियलाइज़ेशन का उपयोग नहीं करेंगे। इसलिए दिशानिर्देश कोष्ठक आरंभीकरण का उपयोग करने के लिए कहता है।
celtschk 13

2
मुझे व्यक्तिगत रूप से यह दिशानिर्देश सबसे ज्यादा पसंद आया और यह सामान्य कोड पर भी अच्छा काम करता है। कुछ अपवाद हैं ( T {}या सबसे डरावने पार्स जैसे वाक्यात्मक कारण ), लेकिन सामान्य तौर पर मुझे लगता है कि यह एक अच्छी सलाह है। ध्यान दें कि यह मेरी व्यक्तिपरक राय है, इसलिए दूसरे उत्तरों पर भी एक नज़र होनी चाहिए।
हेलामी

2
@celtschk: यह गैर-प्रतिलिपि योग्य, गैर-चल प्रकार के लिए काम नहीं करेगा; type var{};कर देता है।
५२ पर ildjarn

2
@celtschk: मैं यह नहीं कह रहा हूं कि यह कुछ ऐसा है जो अक्सर होता है, लेकिन यह कम टाइपिंग है और अधिक संदर्भों में काम करता है, इसलिए नकारात्मक पक्ष क्या है?
.िलजार्नर

2
मेरे दिशानिर्देश निश्चित रूप से कॉपी-इनिशियलाइज़ेशन के लिए कभी नहीं बुलाते हैं। ; -]
ildjarn

26

मुझे पूरा यकीन है कि एक सार्वभौमिक दिशानिर्देश कभी नहीं होगा। मेरा दृष्टिकोण हमेशा कर्ली ब्रेसिज़ को याद रखने के लिए उपयोग करना है

  1. इनिशियल लिस्ट कंस्ट्रक्टर अन्य कंस्ट्रक्टरों पर वरीयता लेते हैं
  2. सभी मानक लाइब्रेरी कंटेनर और std :: basic_string में इनिशलाइज़र सूची निर्माता हैं।
  3. घुंघराले ब्रेस इनिशियलाइज़ेशन रूपांतरण को कम करने की अनुमति नहीं देता है।

तो गोल और घुंघराले ब्रेस विनिमेय नहीं हैं। लेकिन यह जानते हुए भी कि वे कहां भिन्न हैं, मुझे ज्यादातर मामलों में (कुछ ऐसे मामलों में जहां मैं संकलक बग नहीं कर सकता) राउंड ब्रैकेट इनिशियलाइज़ेशन पर घुंघराले उपयोग करने की अनुमति देता है।


6
घुंघराले ब्रेसिज़ का नुकसान है कि मैं गलती से सूची निर्माणकर्ता को कॉल कर सकता हूं। गोल कोष्ठक नहीं। क्या यह डिफ़ॉल्ट रूप से गोल कोष्ठक का उपयोग करने का कारण नहीं है?
हेलमी

4
@user: int i = 0;मुझे नहीं लगता कि कोई भी उपयोग करेगाint i{0} वहाँ का , और यह भ्रामक हो सकता है (यह भी, 0यदि प्रकार है int, तो कोई संकीर्णता नहीं होगी )। बाकी सभी चीजों के लिए, मैं जुआनचो की सलाह का पालन करूंगा: {} को प्राथमिकता दें, उन कुछ मामलों से सावधान रहें जहां आपको नहीं करना चाहिए। ध्यान दें कि कई प्रकार नहीं हैं जो कंस्ट्रक्टर के तर्कों के रूप में इनिशियलाइज़र सूची लेंगे, आप कंटेनर और कंटेनर जैसे प्रकार (टपल ...) की उम्मीद कर सकते हैं, लेकिन अधिकांश कोड उपयुक्त निर्माता को कॉल करेंगे।
डेविड रॉड्रिग्ज - dribeas

3
@ user1304032 यह निर्भर करता है कि क्या आप संकुचित होने की परवाह करते हैं। मैं करता हूं, इसलिए मैं संकलक को पसंद करता हूं कि मुझे बताएं कि int i{some floating point}एक त्रुटि है, बजाय चुपचाप रौंदने के।
जुआनकोपनज़ा

3
के बारे में "पसंद {}, कुछ मामलों से सावधान रहें जहाँ आपको नहीं करना चाहिए": कहते हैं कि दो वर्गों के पास एक शब्दार्थ समतुल्य रचनाकार होता है, लेकिन एक वर्ग में एक प्रारंभिक सूची भी होती है। क्या दो समकक्ष निर्माणकर्ताओं को अलग-अलग कहा जाना चाहिए?
हेलमी

3
@ स्वामी: "कहते हैं कि दो वर्गों में एक समान रूप से समतुल्य कंस्ट्रक्टर होता है, लेकिन एक वर्ग में एक इनिशियलाइज़र सूची भी होती है। क्या दो समान कंस्ट्रक्टरों को अलग-अलग कहा जाना चाहिए?" कहते हैं कि मैं सबसे अधिक डरावने पार्स में दौड़ता हूं; यह किसी भी उदाहरण के लिए किसी भी निर्माता पर हो सकता है । इससे बचना बहुत आसान है अगर आप सिर्फ {}"इनिशियलाइज़" का उपयोग करते हैं जब तक कि आप बिल्कुल नहीं कर सकते
निकोल बोलस

16

सामान्य कोड (यानी टेम्प्लेट) के बाहर, आप (और मैं करते हैं) हर जगह ब्रेसिज़ का उपयोग कर सकते हैं । एक फायदा यह है कि यह हर जगह काम करता है, उदाहरण के लिए इन-क्लास इनिशियलाइज़ेशन के लिए भी:

struct foo {
    // Ok
    std::string a = { "foo" };

    // Also ok
    std::string b { "bar" };

    // Not possible
    std::string c("qux");

    // For completeness this is possible
    std::string d = "baz";
};

या फ़ंक्शन तर्क के लिए:

void foo(std::pair<int, double*>);
foo({ 42, nullptr });
// Not possible with parentheses without spelling out the type:
foo(std::pair<int, double*>(42, nullptr));

चरों के लिए मैं T t = { init };या T t { init };शैलियों के बीच ज्यादा ध्यान नहीं देता , मुझे अंतर मामूली लगता है और एक explicitकंस्ट्रक्टर के दुरुपयोग के बारे में एक सहायक संकलक संदेश में सबसे खराब परिणाम देगा ।

ऐसे प्रकारों के लिए जो स्वीकार करते हैं, std::initializer_listहालांकि स्पष्ट रूप से कभी-कभी गैर- std::initializer_listकंस्ट्रक्टरों की आवश्यकता होती है (शास्त्रीय उदाहरण std::vector<int> twenty_answers(20, 42);)। इसके बाद ब्रेसिज़ का उपयोग नहीं करना ठीक है।


जब जेनेरिक कोड (यानी टेम्प्लेट) की बात आती है, तो बहुत अंतिम पैराग्राफ में कुछ चेतावनियां उठानी चाहिए थीं। निम्नलिखित को धयान मे रखते हुए:

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T { std::forward<Args>(args)... } }; }

फिर auto p = make_unique<std::vector<T>>(20, T {});आकार 2 का एक वेक्टर बनाता है, तो Tउदाहरण के लिए है int, या आकार 20 का एक वेक्टर अगर Tहै std::string। एक बहुत बड़ा संकेत यह है कि यहाँ बहुत कुछ गलत हो रहा है, ऐसा नहीं है लक्षण है जो आपको यहाँ बचा सकता है (जैसे कि SFINAE के साथ): std::is_constructibleप्रत्यक्ष-आरंभीकरण के संदर्भ में है, जबकि हम ब्रेस-इनिशियलाइज़ेशन का उपयोग कर रहे हैं, जो डायरेक्ट करने के लिए ख़राब है। इनिशियलाइज़ेशन इफ एंड इफ तभी है जब कोई कंस्ट्रक्टर std::initializer_listहस्तक्षेप नहीं कर रहा है। उसी प्रकारstd::is_convertible कोई मदद नहीं करता है।

मैंने जांच की है कि क्या वास्तव में एक विशेषता को हाथ से रोल करना संभव है जो इसे ठीक कर सकता है लेकिन मैं इसके बारे में अधिक आशावादी नहीं हूं। किसी भी मामले में मुझे नहीं लगता कि हम बहुत याद कर रहे हैं, मुझे लगता है कि तथ्य यह है किmake_unique<T>(foo, bar) एक निर्माण के बराबर में परिणाम T(foo, bar)बहुत सहज है; विशेष रूप से दिया गया है कि make_unique<T>({ foo, bar })काफी भिन्न है और केवल अगर fooऔर समझ में आता हैbar एक ही प्रकार है।

इसलिए जेनेरिक कोड के लिए मैं केवल वैल्यू इनिशियलाइज़ेशन (जैसे T t {};या T t = {};) के लिए ब्रेसिज़ का उपयोग करता हूं , जो बहुत सुविधाजनक है और मुझे लगता है कि C ++ की तरह बेहतर हैT t = T();अन्यथा यह प्रत्यक्ष आरंभीकरण सिंटैक्स (यानी T t(a0, a1, a2);), या कभी-कभी डिफ़ॉल्ट निर्माण है (T t; stream >> t; केवल मामला है जहां मैं उपयोग करता हूं कि मुझे लगता है)।

इसका मतलब यह नहीं है कि सभी ब्रेसिज़ खराब हैं, हालांकि पिछले उदाहरण को फिक्स के साथ देखें:

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T(std::forward<Args>(args)...) }; }

यह अभी भी निर्माण के लिए ब्रेसिज़ का उपयोग करता है std::unique_ptr<T>, भले ही वास्तविक प्रकार टेम्पलेट पैरामीटर पर निर्भर करता है T


@interjay मेरे कुछ उदाहरणों के बजाय या तो होने के make_unique<T>(20u, T {})लिए अहस्ताक्षरित प्रकारों का उपयोग करने की आवश्यकता हो सकती है । विवरण पर भी निश्चित नहीं है। (ध्यान दें कि मैंने प्रत्यक्ष आरंभीकरण बनाम ब्रेस इनिशियलाइज़ेशन जैसे कि परफेक्ट-फ़ॉरवर्डिंग फ़ंक्शंस के संबंध में अपेक्षाओं पर टिप्पणी की।) व्याकरण में सदस्य फ़ंक्शन घोषणाओं के साथ अस्पष्टता से बचने के लिए इन-क्लास इनिशियलाइज़ेशन के रूप में काम करने के लिए निर्दिष्ट नहीं किया गया है। Tunsignedstd::stringstd::string c("qux");
ल्यूक डैंटन

@interjay मैं पहले बिंदु पर आपसे सहमत नहीं हूँ, सूची-आरंभीकरण द्वारा 8.5.4 सूची आरंभीकरण और 13.3.1.7 प्रारंभ की जाँच करने के लिए स्वतंत्र महसूस करें। दूसरे के रूप में, आपको जो मैंने लिखा है (जो कि इन-क्लास इनिशियलाइज़ेशन के बारे में लिखा है) और / या C ++ व्याकरण (जैसे सदस्य-घोषणाकर्ता , जो ब्रेस-या-इक्व-इनिशियल-रेफ़राइज़र का संदर्भ देता है) पर करीब से नज़र डालने की आवश्यकता है ।
ल्यूक डैंटन

हम्म, आप सही कह रहे हैं - मैं पहले जीसीसी 4.5 के साथ परीक्षण कर रहा था जो मुझे पुष्टि कर रहा था कि मैं क्या कह रहा था, लेकिन जीसीसी 4.6 आपके साथ सहमत है। और मैंने इस तथ्य को याद किया कि आप इन-क्लास आरंभीकरण के बारे में बात कर रहे थे। मैं क्षमाप्रार्थी हूं।
०६ पर अंतरजाल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.