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




class c++11 (2)

सी ++ 11 से पहले, हम केवल अभिन्न या गणना प्रकार के स्थिर कॉन्स सदस्यों पर कक्षा में प्रारंभिक प्रदर्शन कर सकते थे। स्ट्रॉस्ट्रप अपने सी ++ एफएक्यू में इस बारे में चर्चा करता है , निम्नलिखित उदाहरण देता है:

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

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

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

हालांकि, सी ++ 11 इन प्रतिबंधों को आराम देता है, गैर-स्थैतिक सदस्यों (§12.6.2 / 8) के कक्षा में प्रारंभ करने की इजाजत देता है:

एक गैर-प्रतिनिधि कन्स्ट्रक्टर में, यदि किसी दिए गए गैर-स्थैतिक डेटा सदस्य या बेस क्लास को mem-startizer-id द्वारा निर्दिष्ट नहीं किया गया है (उस मामले सहित जहां कोई मेम-प्रारंभकर्ता-सूची नहीं है क्योंकि कन्स्ट्रक्टर के पास कोई सीटीओ-प्रारंभकर्ता नहीं है ) और इकाई एक सार वर्ग (10.4) का वर्चुअल बेस क्लास नहीं है, फिर

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

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

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


मुझे लगता है कि टेम्पलेट्स को अंतिम रूप देने से पहले तर्क लिखा जा सकता था। स्थिर सदस्यों के इन-क्लास प्रारंभकर्ताओं के लिए जरूरी "जटिल लिंकर नियम" आवश्यक थे, टेम्पलेट्स के स्थिर सदस्यों का समर्थन करने के लिए सी ++ 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";
}

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

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

अद्यतन करें

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


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

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

यह प्रोग्रामर के लिए भी कुछ और जटिल नियमों का कारण बनता है, लेकिन यह काफी आसान है कि यह एक बड़ा सौदा नहीं है। अतिरिक्त नियम तब आते हैं जब आपके पास एक सदस्य के लिए निर्दिष्ट दो अलग-अलग प्रारंभकर्ता होते हैं:

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

अब, इस बिंदु पर अतिरिक्त नियम इस बात से निपटते हैं कि जब आप गैर-डिफ़ॉल्ट कन्स्ट्रक्टर का उपयोग करते हैं तो प्रारंभ करने के लिए किस मान का उपयोग किया जाता है। इसका उत्तर काफी सरल है: यदि आप किसी ऐसे निर्माता का उपयोग करते हैं जो किसी अन्य मान को निर्दिष्ट नहीं करता है, तो 1234 का उपयोग प्रारंभ करने के लिए किया जाएगा - लेकिन यदि आप किसी अन्य मूल्य को निर्दिष्ट करने वाले निर्माता का उपयोग करते हैं, तो 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






class-members