C ++ 11 गैर-स्थैतिक और गैर-कॉन्स्टेबल सदस्यों की इन-क्लास प्रारंभिक अनुमति देता है। किया बदल गया?


89

C ++ 11 से पहले, हम केवल अभिन्न या गणन प्रकार के स्थिर कॉन्स्ट सदस्यों पर इन-क्लास आरंभीकरण कर सकते थे। Stroustrup अपने C ++ FAQ में इस पर चर्चा करते हैं , निम्न उदाहरण देते हैं:

class Y {
  const int c3 = 7;           // error: not static
  static int c4 = 7;          // error: not const
  static const float c5 = 7;  // error: not integral
};

और निम्नलिखित तर्क:

तो ये असुविधाजनक प्रतिबंध क्यों मौजूद हैं? हेडर फ़ाइल में आमतौर पर एक वर्ग घोषित किया जाता है और हेडर फ़ाइल को आमतौर पर कई अनुवाद इकाइयों में शामिल किया जाता है। हालांकि, जटिल लिंकर नियमों से बचने के लिए, C ++ के लिए आवश्यक है कि प्रत्येक वस्तु की एक अनूठी परिभाषा हो। उस नियम को तोड़ा जाएगा, यदि C ++ को उन संस्थाओं की इन-क्लास परिभाषा की अनुमति दी जाए, जिन्हें स्मृति में वस्तुओं के रूप में संग्रहीत करने की आवश्यकता है।

हालाँकि, C ++ 11 इन प्रतिबंधों को शिथिल करता है, जिससे गैर-स्थैतिक सदस्यों की इन-क्लास आरंभीकरण की अनुमति मिलती है (.612.6.2 / 8):

एक गैर-प्रतिनिधि निर्माणकर्ता में, यदि किसी दिए गए गैर-स्थैतिक डेटा सदस्य या आधार वर्ग को एक मेम-इनिशलाइज़र-आईडी द्वारा निर्दिष्ट नहीं किया जाता है (इस मामले में जहां कोई मेम-इनिशियलाइज़र-सूची नहीं है क्योंकि कंस्ट्रक्टर के पास कोई ctor-initializer नहीं है ) और इकाई एक अमूर्त वर्ग (10.4) का आभासी आधार वर्ग नहीं है, तब

  • यदि इकाई एक गैर-स्थैतिक डेटा सदस्य है जिसमें ब्रेस-या-सम-इनिशलाइज़र है , तो इकाई को 8.5 में निर्दिष्ट के रूप में आरंभीकृत किया जाता है;
  • अन्यथा, यदि इकाई एक भिन्न सदस्य (9.5) है, तो कोई प्रारंभ नहीं किया जाता है;
  • अन्यथा, इकाई डिफ़ॉल्ट-आरंभिक (8.5) है।

धारा 9.4.2 गैर-स्थैतिक सदस्यों के इन-क्लास आरंभीकरण की अनुमति देता है यदि वे constexprविनिर्देशक के साथ चिह्नित हैं ।

तो हमारे पास C ++ 03 में प्रतिबंधों के कारणों का क्या हुआ? क्या हम सिर्फ "जटिल लिंकर नियमों" को स्वीकार करते हैं या कुछ और बदल गया है जो इसे लागू करना आसान बनाता है?


5
कुछ नहीं हुआ। कंपाइलर इन सभी हेडर-ओनली टेम्प्लेट के साथ होशियार हो गए हैं, इसलिए यह अपेक्षाकृत आसान विस्तार है।
54ö तिएब

मेरे आईडीई पर दिलचस्प रूप से पर्याप्त है जब मैं पूर्व C ++ 11 संकलन का चयन करता हूं तो मुझे गैर-स्थैतिक कॉन्स्ट इंटीग्रल सदस्यों को शुरू करने की अनुमति है
डीन पी

जवाबों:


67

संक्षिप्त उत्तर यह है कि उन्होंने लिंकर को उसी के बारे में रखा था, जो कंपाइलर बनाने की कीमत पर पहले की तुलना में अभी भी अधिक जटिल है।

यानी, इसके बजाय लिंकर को छांटने के लिए कई परिभाषाओं के परिणामस्वरूप, यह अभी भी केवल एक परिभाषा में परिणत होता है, और संकलक को इसे सुलझाना पड़ता है।

यह प्रोग्रामर के लिए कुछ और अधिक जटिल नियमों को भी सुलझाता है और साथ ही साथ उसे सुलझाता भी है, लेकिन यह ज्यादातर सरल है कि यह कोई बड़ी बात नहीं है। अतिरिक्त नियम तब आते हैं जब आपके पास एक ही सदस्य के लिए निर्दिष्ट दो अलग-अलग इनिशियलाइज़र होते हैं:

class X { 
    int a = 1234;
public:
    X() = default;
    X(int z) : a(z) {}
};

अब, इस बिंदु पर अतिरिक्त नियम aगैर-डिफ़ॉल्ट कंस्ट्रक्टर का उपयोग करते समय आरंभ करने के लिए किस मूल्य का उपयोग किया जाता है । इसका उत्तर काफी सरल है: यदि आप एक ऐसे निर्माता का उपयोग करते हैं जो किसी अन्य मूल्य को निर्दिष्ट नहीं करता है, तो 1234इसका उपयोग आरंभ करने के लिए किया जाएगा a- लेकिन यदि आप एक ऐसे निर्माता का उपयोग करते हैं जो कुछ अन्य मूल्य को निर्दिष्ट करता 1234है , तो मूल रूप से अनदेखा किया जाता है।

उदाहरण के लिए:

#include <iostream>

class X { 
    int a = 1234;
public:
    X() = default;
    X(int z) : a(z) {}

    friend std::ostream &operator<<(std::ostream &os, X const &x) { 
        return os << x.a;
    }
};

int main() { 
    X x;
    X y{5678};

    std::cout << x << "\n" << y;
    return 0;
}

परिणाम:

1234
5678

1
ऐसा लगता है कि पहले काफी संभव था। इसने बस लिखने वाले का काम कठिन बना दिया। क्या यह उचित कथन है?
अल्लौरकोड

10
@allyourcode: हाँ और नहीं। हां, इसने संकलक को कठिन बना दिया। लेकिन नहीं, क्योंकि यह भी सी ++ विनिर्देश काफ़ी कठिन लिख दिया।
जेरी कॉफिन

क्या कोई अंतर है कि कक्षा के सदस्य को कैसे शुरू किया जाए: int x = 7; या int x {7} ;?
मब्रोस

9

मुझे लगता है कि टेम्प्लेट को अंतिम रूप देने से पहले तर्क को लिखा जा सकता है। सभी "जटिल लिंकर नियम (ओं)" के बाद, स्थैतिक सदस्यों के इन-क्लास शुरुआती के लिए आवश्यक / पहले से ही C ++ 11 के लिए आवश्यक थे कि वे स्थैतिक सदस्यों का समर्थन कर सकें।

विचार करें

struct A { static int s = ::ComputeSomething(); }; // NOTE: This isn't even allowed,
                                                   // thanks @Kapil for pointing that out

// vs.

template <class T>
struct B { static int s; }

template <class T>
int B<T>::s = ::ComputeSomething();

// or

template <class T>
void Foo()
{
    static int s = ::ComputeSomething();
    s++;
    std::cout << s << "\n";
}

संकलक के लिए समस्या सभी तीन मामलों में समान है: किस अनुवाद-इकाई में इसे sशुरू करने के लिए और कोड की परिभाषा का अनुकरण करना चाहिए ? सरल उपाय यह है कि इसे हर जगह फेंक दें और लिंकर को इसे छाँटने दें। इसीलिए लिंकर्स ने पहले ही जैसी चीजों का समर्थन किया __declspec(selectany)। इसके बिना C ++ 03 को लागू करना संभव नहीं होगा। और इसीलिए लिंकर का विस्तार करना आवश्यक नहीं था।

इसे और अधिक स्पष्ट रूप से कहने के लिए: मुझे लगता है कि पुराने मानक में दिया गया तर्क सिर्फ सादा गलत है।


अपडेट करें

जैसा कि कपिल ने बताया, मेरा पहला उदाहरण वर्तमान मानक (C ++ 14) में भी अनुमति नहीं है। मैंने इसे वैसे भी छोड़ दिया, क्योंकि यह आईएमओ कार्यान्वयन (कंपाइलर, लिंकर) के लिए सबसे कठिन मामला है। मेरा कहना है: यहां तक कि उस मामले में कोई भी कठिन नहीं है जो पहले से ही अनुमत है जैसे कि टेम्पलेट्स का उपयोग करते समय।


शर्म की बात यह है कि कोई भी अपवित्र नहीं हुआ, क्योंकि C ++ 11 में से कई विशेषताएं समान हैं, जिनमें पहले से ही आवश्यक क्षमता या अनुकूलन शामिल हैं।
एलेक्स कोर्ट

@AlexCourt ने हाल ही में यह उत्तर लिखा है। सवाल और जेरी का जवाब हालांकि 2012 से है। इसलिए मुझे लगता है कि इसीलिए मेरे जवाब पर ज्यादा ध्यान नहीं दिया गया।
पॉल ग्रोके

1
इससे "संरचना A {स्थिर int s = :: ComputeSomething ();}" की शिकायत नहीं होगी क्योंकि केवल स्थैतिक कॉन्स्ट को कक्षा में आरम्भ किया जा सकता है
PapaDiHatti

8

सिद्धांत रूप में So why do these inconvenient restrictions exist?...कारण मान्य है, लेकिन इसे आसानी से बाईपास किया जा सकता है और यह वही है जो सी ++ 11 करता है।

जब आप कोई फ़ाइल शामिल करते हैं, तो इसमें फ़ाइल शामिल होती है और किसी भी आरंभ को अस्वीकृत करता है। सदस्यों को केवल तब ही इनिशियलाइज़ किया जाता है जब आप क्लास को इंस्टेंट करते हैं।

दूसरे शब्दों में, आरंभीकरण अभी भी कंस्ट्रक्टर के साथ जुड़ा हुआ है, बस संकेतन अलग है और अधिक सुविधाजनक है। यदि निर्माता को नहीं बुलाया जाता है, तो मानों को आरंभिक नहीं किया जाता है।

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

यह सी ++ 11 पर स्ट्रॉस्ट्रुप के स्वयं के FAQ से स्पष्ट है ।


पुन: "यदि निर्माता को नहीं बुलाया जाता है, तो मानों को प्रारंभ नहीं किया जाता है": मैं Y::c3प्रश्न में सदस्य के आरंभीकरण को कैसे रोक सकता हूं ? जैसा कि मैं इसे समझता हूं, c3हमेशा आरंभिक रूप से तब तक किया जाएगा जब तक कि कोई कंस्ट्रक्टर न हो जो घोषणा में दिए गए डिफ़ॉल्ट को ओवरराइड करता है।
पीटर - मोनिका
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.