सूची आरंभीकरण (घुंघराले ब्रेसिज़ का उपयोग करना) विकल्पों की तुलना में बेहतर क्यों है?


405
MyClass a1 {a};     // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);

क्यों?

मुझे SO पर कोई उत्तर नहीं मिला, इसलिए मैं अपने प्रश्न का उत्तर दूं।


12
उपयोग क्यों नहीं auto?
मार्क गार्सिया

33
यह सच है, यह सुविधाजनक है, लेकिन यह मेरी राय में पठनीयता को कम करता है - मुझे यह देखना पसंद है कि कोड पढ़ते समय कोई वस्तु किस प्रकार की होती है। यदि आप 100% सुनिश्चित हैं कि ऑब्जेक्ट किस प्रकार का है, तो ऑटो का उपयोग क्यों करें? और यदि आप सूची आरंभीकरण का उपयोग करते हैं (मेरा उत्तर पढ़ें), तो आप यह सुनिश्चित कर सकते हैं कि यह हमेशा सही है।
ओलेक्सी

102
@ ओलेक्सी: std::map<std::string, std::vector<std::string>>::const_iteratorआपके साथ एक शब्द चाहेगा।
Xeo

9
@ ओलेक्सी मैं इस GotW को पढ़ने की सलाह देता हूं
रैप्ट्ज

17
@ डॉक मैं कहूंगा कि using MyContainer = std::map<std::string, std::vector<std::string>>;यह और भी बेहतर है (विशेषकर जब आप इसे टेम्पलेट कर सकते हैं!)
JAB

जवाबों:


356

मूल रूप से Bjarne Stroustrup के "द C ++ प्रोग्रामिंग लैंग्वेज 4th एडिशन" से कॉपी और पेस्ट करना :

सूची आरंभीकरण संकुचित करने की अनुमति नहीं देता (izationiso.8.5.4)। अर्थात्:

  • एक पूर्णांक को दूसरे पूर्णांक में परिवर्तित नहीं किया जा सकता है जो इसका मान नहीं रख सकता है। उदाहरण के लिए, char to int की अनुमति है, लेकिन char को int नहीं।
  • एक फ़्लोटिंग-पॉइंट वैल्यू को दूसरे फ़्लोटिंग-पॉइंट प्रकार में परिवर्तित नहीं किया जा सकता है जो इसका मान नहीं रख सकता है। उदाहरण के लिए, फ्लोट टू डबल की अनुमति है, लेकिन फ्लोट से डबल नहीं।
  • फ़्लोटिंग-पॉइंट मान को पूर्णांक प्रकार में परिवर्तित नहीं किया जा सकता है।
  • पूर्णांक मान को फ़्लोटिंग-पॉइंट प्रकार में परिवर्तित नहीं किया जा सकता है।

उदाहरण:

void fun(double val, int val2) {

    int x2 = val; // if val==7.9, x2 becomes 7 (bad)

    char c2 = val2; // if val2==1025, c2 becomes 1 (bad)

    int x3 {val}; // error: possible truncation (good)

    char c3 {val2}; // error: possible narrowing (good)

    char c4 {24}; // OK: 24 can be represented exactly as a char (good)

    char c5 {264}; // error (assuming 8-bit chars): 264 cannot be 
                   // represented as a char (good)

    int x4 {2.0}; // error: no double to int value conversion (good)

}

केवल स्थिति है जहाँ = से अधिक पसंद किया जाता है {} का उपयोग करते समय है autoकीवर्ड प्रकार प्रारंभकर्ता द्वारा निर्धारित किया जाता प्राप्त करने के लिए।

उदाहरण:

auto z1 {99};   // z1 is an int
auto z2 = {99}; // z2 is std::initializer_list<int>
auto z3 = 99;   // z3 is an int

निष्कर्ष

जब तक आपके पास एक मजबूत कारण नहीं है तब तक {} विकल्प पर आरंभीकरण को प्राथमिकता दें।


52
तथ्य यह भी है कि उपयोग ()को एक फ़ंक्शन घोषणा के रूप में पार्स किया जा सकता है। यह भ्रामक और असंगत है कि आप कह सकते हैं T t(x,y,z);लेकिन नहीं T t()। और कभी-कभी, आप निश्चित हैं x, आप भी नहीं कह सकते T t(x);
जुआनकोपंजा

84
मैं इस जवाब से पूरी तरह असहमत हूं; ब्रेडेड इनिशियलाइज़ेशन एक पूर्ण गड़बड़ हो जाता है जब आपके पास एक कॉटर को स्वीकार करने के साथ टाइप होता है std::initializer_list। RedXIII इस मुद्दे का उल्लेख करता है (और बस इसे बंद कर देता है), जबकि आप इसे पूरी तरह से अनदेखा करते हैं। A(5,4)और A{5,4}पूरी तरह से विभिन्न कार्यों को कॉल कर सकते हैं, और यह जानना महत्वपूर्ण बात है। यह उन कॉलों में भी परिणत हो सकता है जो अनपेक्षित लगती हैं। यह कहना कि आपको {}डिफ़ॉल्ट रूप से पसंद करना चाहिए इससे लोगों को गलतफहमी होगी कि क्या हो रहा है। हालांकि यह आपकी गलती नहीं है। मुझे व्यक्तिगत रूप से लगता है कि यह एक बेहद खराब सोच वाला फीचर है।
user1520427

13
@ user1520427 यही कारण है कि " जब तक आपके पास एक मजबूत कारण नहीं है " भाग नहीं है
ओलेक्सी

67
हालांकि यह सवाल पुराना है, यह काफी हिट है, इसलिए मैं इसे केवल संदर्भ के लिए यहां जोड़ रहा हूं (मैंने इसे पृष्ठ में कहीं और नहीं देखा है)। C ++ 14 से ब्रेडेड-इन-लिस्ट से ऑटो कटौती के नए नियमों के साथ अब auto var{ 5 }इसे लिखना संभव है और इसे और intअधिक के रूप में नहीं घटाया जाएगा std::initializer_list<int>
एडुआर्डो स्पार्कन डोमिनिकी

12
हाहा, सभी टिप्पणियों से यह अभी भी स्पष्ट नहीं है कि क्या करना है। क्या स्पष्ट है, कि सी ++ विनिर्देश एक गड़बड़ है!
ड्रम

113

सूची आरंभीकरण का उपयोग करने के लाभों के बारे में पहले से ही महान जवाब हैं, हालांकि मेरे अंगूठे का व्यक्तिगत नियम जब भी संभव हो घुंघराले ब्रेसिज़ का उपयोग नहीं करना है, लेकिन इसके बजाय यह वैचारिक अर्थ पर निर्भर करता है:

  • यदि मैं जिस वस्तु का निर्माण कर रहा हूं, वह कंस्ट्रक्टर (जैसे कंटेनर, पीओडी स्ट्रक्चर, एटॉमिक्स, स्मार्ट पॉइंटर्स इत्यादि) में गुजरने वाले मूल्यों को धारण करता है, तो मैं ब्रेसिज़ का उपयोग कर रहा हूं।
  • अगर कंस्ट्रक्टर एक सामान्य फ़ंक्शन कॉल जैसा दिखता है (यह कुछ अधिक या कम जटिल ऑपरेशन करता है जो तर्कों द्वारा पैरामीट्रिक किए जाते हैं) तो मैं सामान्य फ़ंक्शन कॉल सिंटैक्स का उपयोग कर रहा हूं।
  • डिफ़ॉल्ट आरंभीकरण के लिए मैं हमेशा घुंघराले ब्रेसिज़ का उपयोग करता हूं।
    एक के लिए, इस तरह से मुझे हमेशा यकीन है कि वस्तु चाहे वह उदाहरण के लिए "वास्तविक" वर्ग हो, चाहे वह किसी भी रूप में या एक बिल्डिन / पीओडी प्रकार के साथ एक "वास्तविक" वर्ग हो, चाहे वह प्रारंभिक हो। दूसरा यह है - ज्यादातर मामलों में - पहले नियम के अनुरूप, एक डिफ़ॉल्ट प्रारंभिक वस्तु अक्सर एक "खाली" वस्तु का प्रतिनिधित्व करती है।

मेरे अनुभव में, इस नियम को डिफ़ॉल्ट रूप से घुंघराले ब्रेसिज़ का उपयोग करने की तुलना में बहुत अधिक लगातार लागू किया जा सकता है, लेकिन स्पष्ट रूप से सभी अपवादों को याद रखना जब उनका उपयोग नहीं किया जा सकता है या लघुकोष्ठक के साथ "सामान्य" फ़ंक्शन-कॉल सिंटैक्स से अलग अर्थ हो सकता है। (एक अलग अधिभार कहते हैं)।

यह उदाहरण के लिए मानक पुस्तकालय प्रकारों के साथ अच्छी तरह से फिट बैठता है std::vector:

vector<int> a{10,20};   //Curly braces -> fills the vector with the arguments

vector<int> b(10,20);   //Parentheses -> uses arguments to parametrize some functionality,                          
vector<int> c(it1,it2); //like filling the vector with 10 integers or copying a range.

vector<int> d{};      //empty braces -> default constructs vector, which is equivalent
                      //to a vector that is filled with zero elements

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

8
@ मिखाइल: यह निश्चित रूप से निरर्थक है, लेकिन यह मेरी आदत है कि मैं हमेशा स्थानीय चर आरंभीकरण को स्पष्ट कर सकता हूं। जैसा कि मैंने लिखा है, यह मुख्य रूप से कंसिस्टेंसी के बारे में है इसलिए मैं इसे नहीं भूलता, जब यह मायने रखता है। यह निश्चित रूप से कुछ भी नहीं है जिसका मैं एक कोड समीक्षा में उल्लेख करूंगा या एक स्टाइल गाइड में डालूंगा।
माइक एमबी

4
सुंदर स्वच्छ नियम।
laike9m

5
यह अब तक का सबसे अच्छा जवाब है। {} विरासत की तरह है - दुरुपयोग करने के लिए आसान, कोड को समझने के लिए कठिन है।
यूकेमोनी

2
@ माइक उदाहरण: const int &b{}<- एक असंबद्ध संदर्भ बनाने की कोशिश नहीं करता है, लेकिन यह एक अस्थायी पूर्णांक वस्तु से बांधता है। दूसरा उदाहरण: struct A { const int &b; A():b{} {} };<- एक असंगठित संदर्भ बनाने की कोशिश नहीं करता है (जैसा ()कि करेगा), लेकिन इसे एक अस्थायी पूर्णांक ऑब्जेक्ट पर बाँध दें, और फिर इसे झूलना छोड़ दें। GCC भी -Wallदूसरे उदाहरण के लिए चेतावनी नहीं देता है।
जोहान्स स्काउब -

91

वहाँ करने के लिए उपयोग ब्रेस आरंभीकरण कई कारण हैं, लेकिन आप जानकारी होनी चाहिए कि निर्माता अन्य निर्माताओं के लिए पसंद किया जाता है , अपवाद डिफ़ॉल्ट-निर्माता जा रहा है। इससे कंस्ट्रक्टर और टेम्प्लेट के साथ समस्याएं होती हैं, जहां टाइप कंस्ट्रक्टर या तो इनिशियलाइज़र लिस्ट हो सकता है या प्लेन ओल्ड कॉटर।initializer_list<>T

struct Foo {
    Foo() {}

    Foo(std::initializer_list<Foo>) {
        std::cout << "initializer list" << std::endl;
    }

    Foo(const Foo&) {
        std::cout << "copy ctor" << std::endl;
    }
};

int main() {
    Foo a;
    Foo b(a); // copy ctor
    Foo c{a}; // copy ctor (init. list element) + initializer list!!!
}

यह मानते हुए कि ऐसे वर्गों से आपका सामना नहीं होता है, इसलिए यह बहुत कम कारण है कि आप इंटेस्टाइज़र सूची का उपयोग न करें।


20
जेनेरिक प्रोग्रामिंग में यह एक बहुत ही महत्वपूर्ण बिंदु है। जब आप टेम्पलेट्स लिखते हैं, नहीं braced-init-सूची (के लिए मानक के नाम का उपयोग { ... }) जब तक आप चाहते हैं initializer_list(डिफ़ॉल्ट-निर्माण एक वस्तु के लिए अच्छी तरह से है, और शायद) अर्थ विज्ञान।
Xeo

82
मुझे ईमानदारी से समझ नहीं आ रहा है कि std::initializer_listनियम क्यों मौजूद है - यह सिर्फ भाषा में भ्रम और गड़बड़ जोड़ता है। Foo{{a}}यदि आप std::initializer_listकंस्ट्रक्टर चाहते हैं तो क्या करना गलत है ? ऐसा लगता है कि std::initializer_listअन्य सभी अधिभार पर पूर्वता लेने की तुलना में समझना बहुत आसान है ।
user1520427

5
उपरोक्त टिप्पणी के लिए +1, क्योंकि इसका वास्तव में गड़बड़ मुझे लगता है !! यह तर्क नहीं है; Foo{{a}}मेरे लिए कुछ तर्क का अनुसरण Foo{a}करता है , जो कि अधिक से अधिक इंटेस्टाइज़र सूची में बदल जाता है (अप उपयोगकर्ता को लगता है कि हम ...)
गेब्रियल

32
मूल रूप से C ++ 11 एक मेस को दूसरी मेस के साथ बदल देता है। ओह, क्षमा करें कि यह इसे प्रतिस्थापित नहीं करता है - यह इसमें जोड़ता है। यदि आप ऐसी कक्षाओं का सामना नहीं करते हैं तो आप कैसे जान सकते हैं? क्या आप शुरू करता है, तो बिना std::initializer_list<Foo> निर्माता, लेकिन यह किया जा रहा है जोड़ा को Fooइसके इंटरफेस विस्तार करने के लिए कुछ बिंदु पर वर्ग? फिर Fooवर्ग के उपयोगकर्ताओं पर शिकंजा कसा जाता है।
दस्तावेज़

10
.. "ब्रेस आरंभीकरण का उपयोग करने के लिए कई कारण" क्या हैं? यह उत्तर एक कारण ( initializer_list<>) को इंगित करता है, जिसे यह वास्तव में योग्य नहीं कहता कि यह किसे पसंद किया जाता है, और फिर एक अच्छे मामले का उल्लेख करने के लिए आगे बढ़ता है जहां वह पसंद नहीं किया जाता है। मुझे क्या याद आ रहा है कि ~ 30 अन्य लोग (2016-04-21 के अनुसार) सहायक पाए गए?
dwanderson

0

यह केवल तब तक सुरक्षित रहता है जब तक कि आप निर्माण नहीं करते हैं -कभी ऐसा नहीं होता जैसे कि क्रोमियम में Google करता है। यदि आप करते हैं, तो यह कम सुरक्षित है। उस ध्वज के बिना केवल असुरक्षित मामले C ++ 20 द्वारा तय किए जाएंगे।

नोट: ए) घुंघराले ब्रैकेट सुरक्षित हैं क्योंकि वे संकीर्ण होने की अनुमति नहीं देते हैं। बी) घुंघराले ब्रैकर्स कम सुरक्षित होते हैं क्योंकि वे निजी या हटाए गए कंस्ट्रक्टरों को बायपास कर सकते हैं, और स्पष्ट रूप से चिह्नित कंस्ट्रक्टरों को कॉल कर सकते हैं।

उन दो संयुक्त का मतलब है कि वे सुरक्षित हैं अगर अंदर क्या है आदिम स्थिरांक, लेकिन कम सुरक्षित अगर वे वस्तुएं हैं (हालांकि C ++ 1 में तय की गई हैं)


मैंने Goldbolt.org के आसपास "स्पष्ट" या "निजी" कंस्ट्रक्टरों को नूडल देने की कोशिश की, जो प्रदान किए गए नमूना कोड का उपयोग करके और एक या दूसरे को निजी या स्पष्ट बनाकर उपयुक्त कंपाइलर त्रुटि के साथ पुरस्कृत किया गया। देखभाल के लिए कुछ नमूना कोड के साथ वापस?
मार्क स्टायर

यह C ++ 20 के लिए प्रस्तावित मुद्दे के लिए ठीक है: open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1008r1.pdf
एलन जेन्सेन

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