प्राइवेट कंस्ट्रक्टर कब प्राइवेट कंस्ट्रक्टर नहीं है?


92

मान लें कि मेरे पास एक प्रकार है और मैं इसका डिफ़ॉल्ट कंस्ट्रक्टर निजी बनाना चाहता हूं। मैं निम्नलिखित लिखता हूं:

class C {
    C() = default;
};

int main() {
    C c;           // error: C::C() is private within this context (g++)
                   // error: calling a private constructor of class 'C' (clang++)
                   // error C2248: 'C::C' cannot access private member declared in class 'C' (MSVC)
    auto c2 = C(); // error: as above
}

महान।

लेकिन फिर, कंस्ट्रक्टर निजी रूप से उतना नहीं निकला जितना मैंने सोचा था कि यह था:

class C {
    C() = default;
};

int main() {
    C c{};         // OK on all compilers
    auto c2 = C{}; // OK on all compilers
}    

यह मुझे बहुत ही आश्चर्यजनक, अप्रत्याशित और स्पष्ट रूप से अवांछित व्यवहार के रूप में प्रभावित करता है। यह ठीक क्यों है?


25
क्या C c{};प्रारंभिक आरंभीकरण नहीं है, इसलिए किसी भी निर्माता को नहीं कहा जाता है?
नेथनऑलिवर

5
@NathanOliver ने क्या कहा। आपके पास उपयोगकर्ता-प्रदान करने वाला कंस्ट्रक्टर नहीं है, इसलिए Cयह एक समुच्चय है।
१०:३५ पर केरेक एसबी

5
@KerrekSB उसी समय, यह मेरे लिए काफी आश्चर्यजनक था कि उपयोगकर्ता स्पष्ट रूप से एक ctor घोषित कर उस ctor उपयोगकर्ता को प्रदान नहीं करता है।
Angew अब

1
@ यही कारण है कि हम सब यहाँ हैं :)
बैरी

2
@ अगर यह एक सार्वजनिक =defaultctor था, तो यह अधिक उचित प्रतीत होगा। लेकिन निजी =defaultकॉटर एक महत्वपूर्ण चीज की तरह लगता है जिसे नजरअंदाज नहीं किया जाना चाहिए। क्या अधिक है, class C { C(); } inline C::C()=default;काफी अलग होना कुछ आश्चर्यजनक है।
यक्क - एडम नेवरामॉन्ट

जवाबों:


61

चाल C ++ 14 8.4.2 / 5 [dcl.fct.def.default] में है]

... एक फ़ंक्शन उपयोगकर्ता-प्रदान किया जाता है यदि यह उपयोगकर्ता द्वारा घोषित किया गया है और इसकी पहली घोषणा पर स्पष्ट रूप से डिफ़ॉल्ट या हटा नहीं है। ...

जिसका अर्थ है कि Cके डिफ़ॉल्ट निर्माता वास्तव में है नहीं , उपयोगकर्ता द्वारा प्रदान की है क्योंकि यह स्पष्ट रूप से अपनी पहली घोषणा में चूक गया था। इस प्रकार, Cकोई उपयोगकर्ता-प्रदान करने वाले निर्माता नहीं हैं और इसलिए यह प्रति 8.5.1 / 1 [dcl.init.aggr] प्रति कुल है:

एक कुल एक सरणी या एक वर्ग (धारा 9) कोई उपयोगकर्ता द्वारा प्रदान की कंस्ट्रक्टर्स साथ (12.1), कोई निजी या संरक्षित गैर स्थिर डेटा सदस्यों (क्लॉज 11), कोई आधार वर्ग (खंड 10), और कोई आभासी कार्यों (10.3 है )।


13
वास्तव में, एक छोटा मानक दोष: तथ्य यह है कि डिफ़ॉल्ट ctor निजी था प्रभाव में इस संदर्भ में नजरअंदाज कर दिया।
यक्क - एडम नेवरामॉन्ट

2
@ यक मैं उस जज के योग्य नहीं लगता। हालांकि, उपयोगकर्ता द्वारा प्रदान किए जाने वाले ctor के बारे में शब्द बहुत जानबूझकर दिखाई देते हैं।
Angew अब

1
@ यक: ठीक है, हाँ और नहीं। यदि कक्षा में कोई डेटा सदस्य हैं, तो आपके पास उन निजी बनाने का मौका होगा। डेटा सदस्यों के बिना, ऐसी बहुत कम स्थितियाँ होती हैं जहाँ यह स्थिति किसी को भी प्रभावित करती है।
Kerrek SB

2
@KerrekSB यह मायने रखता है कि आप कक्षा को "एक्सेस टोकन" की तरह उपयोग करने का प्रयास कर रहे हैं, उदाहरण के लिए नियंत्रित करना कि कौन एक फ़ंक्शन को कॉल कर सकता है जो वर्ग का एक ऑब्जेक्ट बना सकता है।
Angew अब SO

5
@ यक और भी दिलचस्प बात यह है कि C{}अगर कंस्ट्रक्टर deleteडी है तो भी काम करता है ।
बैरी

56

आप डिफॉल्ट कंस्ट्रक्टर को कॉल नहीं कर रहे हैं, आप एग्रीगेट टाइप पर एग्रीगेट इनिशियलाइज़ेशन का उपयोग कर रहे हैं। अलग-अलग प्रकारों को एक डिफ़ॉल्ट निर्माणकर्ता की अनुमति दी जाती है, इसलिए जब तक यह पहली बार घोषित किया जाता है, तब तक यह चूक हो जाती है:

से [dcl.init.aggr] / 1 :

एक समुच्चय एक सरणी या एक वर्ग (क्लाज [वर्ग]) है

  • कोई उपयोगकर्ता-प्रदान किए गए कंस्ट्रक्टर ([class.ctor]) (एक आधार वर्ग से विरासत में मिले ([नामस्थान)। सहित),
  • कोई भी निजी या संरक्षित गैर-स्थैतिक डेटा सदस्य (क्लाज [वर्ग]]),
  • कोई वर्चुअल फ़ंक्शंस ([class.virtual]), और
  • कोई वर्चुअल, निजी या संरक्षित आधार वर्ग ([class.mi]) नहीं।

और [dcl.fct.def.default] / 5 से

स्पष्ट रूप से डिफॉल्ट किए गए फ़ंक्शंस और अंतर्निहित रूप से घोषित फ़ंक्शंस को सामूहिक रूप से डिफॉल्ट फ़ंक्शंस कहा जाता है, और कार्यान्वयन उनके लिए निहित परिभाषाएँ प्रदान करेगा ([class.ctor] [class.dtor], [class.copy]), जिसका अर्थ हो सकता है कि उन्हें हटा दिया गया हो। । एक फ़ंक्शन उपयोगकर्ता-प्रदान किया गया है यदि यह उपयोगकर्ता-घोषित है और इसकी पहली घोषणा पर स्पष्ट रूप से डिफ़ॉल्ट या हटा नहीं है। एक उपयोगकर्ता द्वारा प्रदान किया गया स्पष्ट रूप से डिफ़ॉल्ट रूप से फ़ंक्शन (यानी, इसकी पहली घोषणा के बाद स्पष्ट रूप से डिफ़ॉल्ट) उस बिंदु पर परिभाषित किया गया है जहां यह स्पष्ट रूप से डिफ़ॉल्ट रूप से डिफ़ॉल्ट है; यदि इस तरह के फ़ंक्शन को स्पष्ट रूप से हटाए जाने के रूप में परिभाषित किया गया है, तो प्रोग्राम बीमार है।[नोट: इसकी पहली घोषणा के बाद किसी फ़ंक्शन को डिफ़ॉल्ट के रूप में घोषित करना एक कुशल कोड बेस के लिए एक स्थिर बाइनरी इंटरफ़ेस को सक्षम करते हुए कुशल निष्पादन और संक्षिप्त परिभाषा प्रदान कर सकता है। - अंतिम नोट]

इस प्रकार, एक समुच्चय के लिए हमारी आवश्यकताएं हैं:

  • कोई गैर-सार्वजनिक सदस्य नहीं
  • कोई वर्चुअल फ़ंक्शन नहीं
  • कोई वर्चुअल या गैर-सार्वजनिक आधार वर्ग नहीं
  • कोई उपयोगकर्ता-प्रदान किए गए निर्माणकर्ता को विरासत में या अन्यथा, जो केवल निर्माणकर्ताओं को अनुमति देता है:
    • स्पष्ट रूप से घोषित, या
    • एक ही समय में स्पष्ट रूप से घोषित और परिभाषित किया गया।

C इन सभी आवश्यकताओं को पूरा करता है।

स्वाभाविक रूप से, आप केवल एक खाली डिफ़ॉल्ट कंस्ट्रक्टर प्रदान करके, या इसे घोषित करने के बाद कंस्ट्रक्टर को डिफ़ॉल्ट के रूप में परिभाषित करके इस झूठे डिफ़ॉल्ट निर्माण व्यवहार से छुटकारा पा सकते हैं:

class C {
    C(){}
};
// --or--
class C {
    C();
};
inline C::C() = default;

2
मुझे यह उत्तर कुछ हद तक अंगे द्वारा बेहतर लगता है, लेकिन मुझे लगता है कि इसे शुरू में दो वाक्यों में एक सारांश से लाभ होगा।
PJTraill

7

Angew's और jaggedSpire के उत्तर उत्कृष्ट हैं और लागू होते हैं। तथा। तथा

हालाँकि, में , चीजें थोड़ी बदल जाती हैं और ओपी में उदाहरण अब संकलित नहीं होगा:

class C {
    C() = default;
};

C p;          // always error
auto q = C(); // always error
C r{};        // ok on C++11 thru C++17, error on C++20
auto s = C{}; // ok on C++11 thru C++17, error on C++20

जैसा कि दो उत्तरों से बताया गया है, इसका कारण यह है कि बाद के दो घोषणापत्र काम करते हैं क्योंकि Cयह एक एकत्रीकरण है और यह समग्र-आरंभीकरण है। हालांकि, की वजह से P1008 , के लिए C में कुल परिवर्तन की ++ 20 परिभाषा (ओपी से बहुत भिन्न नहीं एक प्रेरित उदाहरण का उपयोग), से [dcl.init.aggr] / 1 :

एक समुच्चय एक सरणी या एक वर्ग ([वर्ग]) है

  • कोई उपयोगकर्ता-घोषित या विरासत में दिए गए निर्माणकर्ता ([class.ctor]),
  • कोई निजी या संरक्षित प्रत्यक्ष गैर-स्थैतिक डेटा सदस्य ([class.access]),
  • कोई वर्चुअल फ़ंक्शंस ([class.virtual]), और
  • कोई वर्चुअल, निजी या संरक्षित आधार वर्ग ([class.mi]) नहीं।

जोर मेरा। अब आवश्यकता नहीं है उपयोगकर्ता के घोषित कंस्ट्रक्टर्स, जबकि यह होना करने के लिए (के रूप में दोनों उपयोगकर्ताओं को अपने जवाब में अदालत में तलब और के लिए ऐतिहासिक दृष्टि से देखी जा सकती है इस्तेमाल किया सी ++ 11 , सी ++ 14 , और सी ++ 17 ) कोई उपयोगकर्ता द्वारा प्रदान की कंस्ट्रक्टर्स । के लिए डिफ़ॉल्ट निर्माता Cउपयोगकर्ता-घोषित है, लेकिन उपयोगकर्ता-प्रदत्त नहीं है, और इसलिए C ++ 20 में कुल होना बंद हो जाता है।


यहाँ कुल परिवर्तनों का एक और उदाहरण दिया गया है:

class A { protected: A() { }; };
struct B : A { B() = default; };
auto x = B{};

BC ++ 11 या C ++ 14 में एक समुच्चय नहीं था क्योंकि इसका आधार वर्ग है। नतीजतन, B{}बस डिफ़ॉल्ट कंस्ट्रक्टर (उपयोगकर्ता द्वारा घोषित नहीं बल्कि उपयोगकर्ता-प्रदान) को आमंत्रित करता है, जिसकी पहुंच Aडिफॉल्ट डिफॉल्ट कंस्ट्रक्टर तक होती है।

C ++ 17 में, P0017 के परिणामस्वरूप , आधार वर्गों के लिए अनुमति देने के लिए समुच्चय बढ़ाए गए थे। BC ++ 17 में एक एग्रीगेट है, जिसका अर्थ है कि B{}एग्रीगेट-इनिशियलाइज़ेशन जिसे सब-सबजेक्ट्स को इनिशियलाइज़ करना है - जिसमें Aसबोबिज भी शामिल है। लेकिन क्योंकि Aडिफॉल्ट कंस्ट्रक्टर संरक्षित है, हमारे पास इसकी पहुंच नहीं है, इसलिए यह इनिशियलाइज़ेशन बीमार है।

C ++ 20 में, Bउपयोगकर्ता के घोषित कंस्ट्रक्टर के कारण, यह फिर से एग्रीगेट होना बंद हो जाता है, इसलिए B{}डिफॉल्ट कंस्ट्रक्टर को इनवॉइस करने के लिए वापस लौटता है और यह फिर से अच्छी तरह से बनाई गई इनिशियलाइज़ेशन है।

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