मैं कक्षा में गैर-स्थिर स्थैतिक सदस्य या स्थिर सरणी को आरंभ क्यों नहीं कर सकता?


116

मैं किसी वर्ग में गैर-कॉन्स्टेबल staticसदस्य या staticसरणी को इनिशियलाइज़ क्यों नहीं कर सकता ?

class A
{
    static const int a = 3;
    static int b = 3;
    static const int c[2] = { 1, 2 };
    static int d[2] = { 1, 2 };
};

int main()
{
    A a;

    return 0;
}

संकलक त्रुटियों के बाद समस्याएँ:

g++ main.cpp
main.cpp:4:17: error: ISO C++ forbids in-class initialization of non-const static member b
main.cpp:5:26: error: a brace-enclosed initializer is not allowed here before ‘{’ token
main.cpp:5:33: error: invalid in-class initialization of static data member of non-integral type const int [2]’
main.cpp:6:20: error: a brace-enclosed initializer is not allowed here before ‘{’ token
main.cpp:6:27: error: invalid in-class initialization of static data member of non-integral type int [2]’

मेरे दो सवाल हैं:

  1. मैं staticकक्षा में डेटा सदस्यों को प्रारंभिक क्यों नहीं कर सकता ?
  2. मैं staticकक्षा में सरणियों को आरंभीकृत क्यों नहीं कर सकता , यहाँ तक कि constसरणी भी ?

1
मुझे लगता है कि मुख्य कारण यह है कि सही पाने के लिए मुश्किल है। सिद्धांत रूप में, आप शायद वही कर सकते हैं जिसके बारे में आप बात कर रहे हैं, लेकिन कुछ अजीब दुष्प्रभाव होंगे। जैसे अगर आपके सरणी उदाहरण की अनुमति थी, तो आप A :: c [0] का मान प्राप्त करने में सक्षम हो सकते हैं, लेकिन एक समारोह में A :: c पास करने में सक्षम नहीं होंगे क्योंकि इसके लिए एक पते की आवश्यकता होगी, और संकलन-समय स्थिरांक का कोई पता नहीं होता है। C ++ 11 ने कॉन्स्टैक्स के उपयोग से कुछ को सक्षम किया है।
वॉन काटो

शानदार सवाल और जवाब। लिंक जिसने मेरी मदद की: msdn.microsoft.com/en-us/library/0e5kx78b.aspx
ETFovac

जवाबों:


144

मैं staticकक्षा में डेटा सदस्यों को प्रारंभिक क्यों नहीं कर सकता ?

C ++ मानक केवल स्थिर स्थिर अभिन्न या गणना प्रकार को वर्ग के अंदर आरंभ करने की अनुमति देता है। यही कारण aहै कि इनिशियलाइज़ होने की अनुमति है जबकि अन्य नहीं हैं।

संदर्भ:
C ++ 03
9.4.2 स्टेटिक डेटा सदस्य 9.44

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

अभिन्न प्रकार क्या हैं?

C ++ 03 3.9.1 मौलिक प्रकार
Fund7

प्रकार बूल, चार, wchar_t, और हस्ताक्षरित और अहस्ताक्षरित पूर्णांक प्रकार को सामूहिक रूप से अभिन्न प्रकार कहा जाता है। 4) अभिन्न प्रकार का एक पर्याय पूर्णांक प्रकार है।

पाद लेख:

43) इसलिए, गणना (7.2) अभिन्न नहीं हैं; हालाँकि, गणन को बढ़ावा दिया जा सकता है int, अहस्ताक्षरित int, लंबा या अहस्ताक्षरित, जैसा कि 4.5 में निर्दिष्ट है।

युक्ति:

आप अपनी कक्षा परिभाषा के अंदर एक सरणी को इनिशियलाइज़ करने के लिए एनम ट्रिक का उपयोग कर सकते हैं ।

class A 
{
    static const int a = 3;
    enum { arrsize = 2 };

    static const int c[arrsize] = { 1, 2 };

};

मानक यह अनुमति क्यों नहीं देता है?

बज्ने ने इसे यहाँ स्पष्ट रूप से समझाया है :

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

केवल static constअभिन्न प्रकार और एनमों को इन-क्लास इनिशियलाइज़ेशन की अनुमति क्यों है ?

उत्तर छिपा हुआ है बज्ने के उद्धरण में इसे बारीकी से पढ़ा गया है,
"सी ++ के लिए आवश्यक है कि प्रत्येक वस्तु की एक अनूठी परिभाषा हो। यदि नियम ++ को उन संस्थाओं की वर्गीय परिभाषा की अनुमति दी जाए जिन्हें स्मृति में वस्तुओं के रूप में संग्रहीत करने की आवश्यकता होती है।"

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

यहाँ यह ध्यान देने योग्य है कि यदि static constअभिन्न मूल्यों में इन-क्लास इनिशियलाइज़ेशन हो सकता है, तो भी ऐसे चरों का पता लगाने की अनुमति नहीं है। एक स्थैतिक सदस्य का पता ले सकता है यदि (और केवल अगर) तो इसकी एक आउट-ऑफ-क्लास परिभाषा है। यह ऊपर दिए गए कारणों को मान्य करता है।

एनमों को इसकी अनुमति है क्योंकि एन्यूमरेटेड प्रकार के मूल्यों का उपयोग उन जगहों पर किया जा सकता है जहां इन्ट्स अपेक्षित हैं। ऊपर उद्धरण देखें


यह C ++ 11 में कैसे बदलता है?

सी ++ 11 कुछ हद तक प्रतिबंध को शांत करता है।

C ++ 11 9.4.2 स्टेटिक डेटा सदस्य
113

यदि एक स्थैतिक डेटा सदस्य कास्ट शाब्दिक प्रकार का है, तो क्लास डेफिनेशन में इसकी घोषणा एक ब्रेस-या-इक्विल-इनिशलाइज़र निर्दिष्ट कर सकती है जिसमें प्रत्येक इनिशियलाइज़र-क्लॉज जो एक असाइनमेंट-एक्सप्रेशन है, एक स्थिर अभिव्यक्ति है। शाब्दिक प्रकार का एक स्थैतिक डेटा सदस्य वर्ग परिभाषा में घोषित किया जा सकता है constexpr specifier;यदि ऐसा है, तो इसकी घोषणा एक ब्रेस-या-समतुल्य-आरंभक निर्दिष्ट करेगी जिसमें प्रत्येक आरंभिक-खंड जो एक असाइनमेंट-एक्सप्रेशन हैएक निरंतर अभिव्यक्ति है। [नोट: इन दोनों मामलों में, सदस्य निरंतर अभिव्यक्तियों में दिखाई दे सकता है। -एंड नोट] सदस्य को अभी भी नाम स्थान के दायरे में परिभाषित किया जाएगा यदि यह प्रोग्राम में उपयोग किया जाता है और नेमस्पेस गुंजाइश परिभाषा में इनिशियलाइज़र नहीं होगा।

इसके अलावा, सी ++ 11 जाएगा अनुमति देते हैं (§12.6.2.8) एक गैर स्थिर डेटा सदस्य जहां यह घोषित किया जाता है प्रारंभ (इसके मुक़दमे में)। यह बहुत आसान उपयोगकर्ता शब्दार्थ का मतलब होगा।

ध्यान दें कि इन सुविधाओं को अभी तक नवीनतम gcc 4.7 में लागू नहीं किया गया है, इसलिए आपको अभी भी संकलन त्रुटियां मिल सकती हैं।


7
C ++ 11 में चीजें अलग हैं। उत्तर अद्यतन का उपयोग कर सकता है।
bames53

4
यह सच प्रतीत नहीं होता है: "ध्यान दें कि केवल स्थैतिक कांस्टेबल पूर्णांक को संकलित समय स्थिरांक के रूप में माना जा सकता है। संकलक जानता है कि पूर्णांक मान कभी भी नहीं बदलेगा और इसलिए यह अपना जादू लागू कर सकता है और अनुकूलन कर सकता है, संकलक बस ऐसे वर्ग के सदस्यों को इनलाइन करके, वे अब मेमोरी में संग्रहीत नहीं हैं , " क्या आप सुनिश्चित हैं कि वे आवश्यक रूप से मेमोरी में संग्रहीत नहीं हैं? यदि मैं सदस्यों के लिए परिभाषाएँ प्रदान करूँ तो क्या होगा? क्या &memberलौटेगा?
नवाज

2
@ मोती: हाँ। यही मेरा सवाल है। इसलिए C ++ केवल अभिन्न प्रकारों के लिए इन-क्लास आरंभीकरण की अनुमति क्यों देता है, आपके उत्तर का सही उत्तर नहीं दिया गया है। सोचें कि यह static const char*सदस्य के लिए आरंभीकरण की अनुमति क्यों नहीं देता है ?
नवाज

3
@ नवाज़: क्योंकि C ++ 03 ने केवल स्थैतिक और कांस्टेबल इंटीग्रल और कास्ट एन्यूमरेशन टाइप के लिए कंटीन्युअस -इनिशियलाइज़र की अनुमति दी है और कोई अन्य प्रकार नहीं, C ++ 11 इसे एक कॉस्टल शाब्दिक प्रकार तक बढ़ाता है जो इन-क्लास इनिशियलाइज़ेशन-लिमिटेशन के मानदंडों को शिथिल करता है। C ++ 03 में शायद एक ओवरसाइट था जिसने एक बदलाव का वारंट दिया था और इसलिए C ++ 11 में सुधार किया गया था, अगर बदलाव के लिए कोई पारंपरिक सामरिक कारण हैं जो मैं उनके बारे में नहीं जानता हूं। यदि आप किसी भी साझा करने के लिए स्वतंत्र महसूस करते हैं उन्हें।
आलोक सेव

4
आपके द्वारा उल्लिखित "वर्कअराउंड" बराबर जी + के साथ काम नहीं कर रहा है
इमिलिंड

4

यह साधारण लिंकर्स के पुराने दिनों से एक राहत है। आप वर्कअराउंड के रूप में स्टैटिक विधियों में स्थिर वैरिएबल का उपयोग कर सकते हैं:

// header.hxx
#include <vector>

class Class {
public:
    static std::vector<int> & replacement_for_initialized_static_non_const_variable() {
        static std::vector<int> Static {42, 0, 1900, 1998};
        return Static;
    }
};

int compilation_unit_a();

तथा

// compilation_unit_a.cxx
#include "header.hxx"

int compilation_unit_a() {  
    return Class::replacement_for_initialized_static_non_const_variable()[1]++;
}

तथा

// main.cxx
#include "header.hxx"

#include <iostream>

int main() {
    std::cout
    << compilation_unit_a()
    << Class::replacement_for_initialized_static_non_const_variable()[1]++
    << compilation_unit_a()
    << Class::replacement_for_initialized_static_non_const_variable()[1]++
    << std::endl;
}

निर्माण:

g++ -std=gnu++0x -save-temps=obj -c compilation_unit_a.cxx 
g++ -std=gnu++0x -o main main.cxx compilation_unit_a.o

Daud:

./main

तथ्य यह है कि यह काम करता है (लगातार, भले ही वर्ग परिभाषा को विभिन्न संकलन इकाइयों में शामिल किया गया है), दिखाता है कि लिंकर आज (जीसीसी 4.9.2) वास्तव में काफी स्मार्ट है।

मजेदार: 0123हाथ 3210पर और x86 पर प्रिंट ।


1

मुझे लगता है कि घोषणाओं और परिभाषाओं को मिलाने से आपको रोकना है। (उन समस्याओं के बारे में सोचें जो फ़ाइल को कई स्थानों पर शामिल करने पर हो सकती हैं।)


0

इसका कारण यह है A::aकि सभी अनुवाद इकाइयों का उपयोग केवल एक ही परिभाषा हो सकती है ।

यदि आप static int a = 3;सभी अनुवाद इकाइयों में शामिल हेडर में एक कक्षा में प्रदर्शन करते हैं तो आपको कई परिभाषाएँ मिलेंगी। इसलिए, स्थैतिक की गैर-आउट-ऑफ-लाइन परिभाषा को जबरन एक संकलक त्रुटि बना दिया जाता है।

इसका उपयोग static inlineया static constउपाय। static inlineयदि यह अनुवाद इकाई में उपयोग किया जाता है तो केवल प्रतीक को संक्षिप्त करता है और यह सुनिश्चित करता है कि लिंकर केवल एक कॉपी का चयन करता है और छोड़ देता है यदि यह एक कॉमडेट समूह में होने के कारण कई अनुवाद इकाइयों में परिभाषित किया गया है। constफ़ाइल स्कोप में कंपाइलर कभी भी प्रतीक का उत्सर्जन नहीं करता है क्योंकि यह हमेशा कोड में तुरंत प्रतिस्थापित externकिया जाता है जब तक कि इसका उपयोग नहीं किया जाता है, जो किसी वर्ग में अनुमति नहीं है।

नोट करने के लिए एक चीज़ को static inline int b;एक परिभाषा के रूप में माना जाता है, जबकि static const int bया static const A b;अभी भी एक घोषणा के रूप में माना जाता है और यदि आप इसे कक्षा के अंदर परिभाषित नहीं करते हैं, तो इसे आउट-ऑफ-लाइन परिभाषित किया जाना चाहिए। दिलचस्प static constexpr A b;रूप से एक परिभाषा के रूप में माना जाता है, जबकि static constexpr int b;एक त्रुटि है और इसमें एक इनिशलाइज़र होना चाहिए (इसका कारण यह है कि वे अब परिभाषा बन जाते हैं और फ़ाइल स्कोप पर किसी भी कॉन्स्टेबल / कॉन्स्ट्रेक्स की परिभाषा की तरह, उन्हें एक इनिशलाइज़र की आवश्यकता होती है, जो एक इंट में नहीं होता है लेकिन एक क्लास प्रकार होता है क्योंकि इसका एक निहितार्थ है = A()जब यह एक परिभाषा है - क्लैंग इस की अनुमति देता है लेकिन जीसीसी को आपको स्पष्ट रूप से आरंभ करने की आवश्यकता होती है या यह एक त्रुटि है। यह इनलाइन के बजाय एक समस्या नहीं है)। static const A b = A();अनुमति नहीं है और होना चाहिए constexprयाinlineकक्षा के प्रकार के साथ एक स्थिर वस्तु के लिए एक इनिशियलाइज़र को अनुमति देने के लिए अर्थात एक घोषणा से अधिक वर्ग प्रकार के एक स्थिर सदस्य बनाने के लिए। तो हाँ कुछ स्थितियों A a;में स्पष्ट रूप से आरंभिक रूप में समान नहीं है A a = A();(पूर्व में एक घोषणा हो सकती है लेकिन अगर केवल उस प्रकार के लिए एक घोषणा की अनुमति है तो उत्तरार्द्ध एक त्रुटि है। उत्तरार्द्ध केवल एक परिभाषा पर उपयोग किया जा सकता है। constexprयह एक परिभाषा बनाता है । )। यदि आप constexprएक डिफॉल्ट कंस्ट्रक्टर का उपयोग करते हैं और निर्दिष्ट करते हैं तो कंस्ट्रक्टर की आवश्यकता होगीconstexpr

#include<iostream>

struct A
{
    int b =2;
    mutable int c = 3; //if this member is included in the class then const A will have a full .data symbol emitted for it on -O0 and so will B because it contains A.
    static const int a = 3;
};

struct B {
    A b;
    static constexpr A c; //needs to be constexpr or inline and doesn't emit a symbol for A a mutable member on any optimisation level
};

const A a;
const B b;

int main()
{
    std::cout << a.b << b.b.b;
    return 0;
}

एक स्थैतिक सदस्य एक सटीक फ़ाइल स्कोप घोषणा है extern int A::a;(जिसे केवल कक्षा में बनाया जा सकता है और लाइन परिभाषाओं में से एक वर्ग में एक स्थिर सदस्य को संदर्भित करना चाहिए और परिभाषाएँ होनी चाहिए और इसमें बाहरी नहीं हो सकता है) जबकि एक गैर-स्थैतिक सदस्य का हिस्सा है एक वर्ग की पूर्ण प्रकार की परिभाषा और बिना नियम के फ़ाइल गुंजाइश घोषणाओं के समान नियम हैं extern। वे अंतर्निहित परिभाषाएँ हैं। तो int i[]; int i[5];एक पुनर्वित्त है जबकि static int i[]; int A::i[5];2 एक्सटर्न्स के विपरीत नहीं है, कंपाइलर अभी भी एक डुप्लिकेट सदस्य का पता लगाएगा यदि आप static int i[]; static int i[5];क्लास में करते हैं।


-3

स्थिर चर एक वर्ग के लिए विशिष्ट हैं। एक उदाहरण के लिए, निर्माता ESPECIALY की विशेषताओं को इनिशियलाइज़ करते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.