c++ स्पष्ट कीवर्ड का क्या अर्थ है?




constructor explicit (9)

explicit शब्दकोष को कन्स्ट्रक्टर को स्पष्ट रूप से बुलाए जाने के लिए उपयोग किया जा सकता है।

class C{
public:
    explicit C(void) = default;
};

int main(void){
    C c();
    return 0;
}

कन्स्ट्रक्टर C(void) सामने explicit कुंजीशब्द संकलक को बताता है कि इस कन्स्ट्रक्टर को केवल स्पष्ट कॉल की अनुमति है।

उपयोगकर्ता-परिभाषित प्रकार कास्ट ऑपरेटरों में explicit कीवर्ड का भी उपयोग किया जा सकता है:

class C{
public:
    explicit inline operator bool(void) const{
        return true;
    }
};

int main(void){
    C c;
    bool b = static_cast<bool>(c);
    return 0;
}

यहां, explicit कुंजीशब्द केवल स्पष्ट रूप से मान्य होने के लिए लागू करता है, इसलिए bool b = c; इस मामले में एक अवैध कलाकार होगा। इन explicit कीवर्ड जैसी परिस्थितियों में प्रोग्रामर को निहित, अनियंत्रित कास्ट से बचने में मदद मिल सकती है। इस उपयोग को C++11 में मानकीकृत किया गया है।

सी ++ में explicit कीवर्ड का क्या अर्थ है?


इस पर पहले ही चर्चा की जा चुकी है ( स्पष्ट कन्स्ट्रक्टर क्या है )। लेकिन मुझे कहना होगा कि इसमें यहां दिए गए विस्तृत विवरणों की कमी है।

इसके अलावा, यह हमेशा एक अच्छा कोडिंग अभ्यास है जो आपके एक तर्क कन्स्ट्रक्टर (जिसमें Arg2, arg3, ... के लिए डिफ़ॉल्ट मान वाले हैं) शामिल हैं। सी ++ के साथ हमेशा की तरह: यदि आप नहीं करते हैं - तो आप चाहेंगे कि आपने किया ...

कक्षाओं के लिए एक और अच्छा अभ्यास कॉपी निर्माण और असाइनमेंट निजी (उर्फ इसे अक्षम करें) बनाना है जब तक कि आपको वास्तव में इसे लागू करने की आवश्यकता न हो। यह उन तरीकों का उपयोग करते समय पॉइंटर्स की अंतिम प्रतियां प्राप्त करता है जो डिफ़ॉल्ट रूप से C ++ आपके लिए बनाएंगे। ऐसा करने का एक और तरीका बूस्ट :: noncopyable से प्राप्त होता है।


स्पष्ट रूपांतरण कन्स्ट्रक्टर (केवल सी ++)

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

class A
{
public:
    A();
    A(int);
    A(const char*, int = 0);
};

निम्नलिखित घोषणाएं कानूनी हैं:

A c = 1;
A d = "Venditti";

पहली घोषणा A c = A( 1 ); बराबर है A c = A( 1 );

यदि आप कक्षा के निर्माता को explicit रूप से घोषित करते हैं, तो पिछली घोषणाएं अवैध होंगी।

उदाहरण के लिए, यदि आप कक्षा को इस प्रकार घोषित करते हैं:

class A
{
public:
    explicit A();
    explicit A(int);
    explicit A(const char*, int = 0);
};

आप केवल उन मानों को असाइन कर सकते हैं जो कक्षा प्रकार के मानों से मेल खाते हैं।

उदाहरण के लिए, निम्नलिखित कथन कानूनी हैं:

  A a1;
  A a2 = A(1);
  A a3(1);
  A a4 = A("Venditti");
  A* p = new A(1);
  A a5 = (A)1;
  A a6 = static_cast<A>(1);

कीवर्ड explicit या तो साथ आता है

  • कक्षा एक्स का एक कन्स्ट्रक्टर जिसे एक्स टाइप करने के लिए पहले (किसी भी एकमात्र) पैरामीटर को निहित रूप से रूपांतरित करने के लिए उपयोग नहीं किया जा सकता है

सी ++ [class.conv.ctor]

1) फंक्शन-विनिर्देशक स्पष्ट के बिना घोषित एक कन्स्ट्रक्टर अपने पैरामीटर के प्रकार से अपने वर्ग के प्रकार में रूपांतरण निर्दिष्ट करता है। इस तरह के एक कन्स्ट्रक्टर को कनवर्टिंग कन्स्ट्रक्टर कहा जाता है।

2) एक स्पष्ट कन्स्ट्रक्टर ऑब्जेक्ट्स को गैर-स्पष्ट कन्स्ट्रक्टर की तरह बनाता है, लेकिन ऐसा करता है जहां प्रत्यक्ष-प्रारंभिक वाक्यविन्यास (8.5) या जहां कास्ट (5.2.9, 5.4) स्पष्ट रूप से उपयोग किया जाता है। एक डिफ़ॉल्ट कन्स्ट्रक्टर एक स्पष्ट निर्माता हो सकता है; ऐसे कन्स्ट्रक्टर का उपयोग डिफ़ॉल्ट-प्रारंभिकरण या वैल्यूएनिलाइजेशन (8.5) करने के लिए किया जाएगा।

  • या एक रूपांतरण समारोह जिसे केवल प्रत्यक्ष प्रारंभिकरण और स्पष्ट रूपांतरण के लिए माना जाता है।

सी ++ [class.conv.fct]

2) एक रूपांतरण समारोह स्पष्ट हो सकता है (7.1.2), इस मामले में इसे केवल प्रत्यक्ष-प्रारंभिक (8.5) के लिए उपयोगकर्ता द्वारा परिभाषित रूपांतरण के रूप में माना जाता है। अन्यथा, उपयोगकर्ता द्वारा परिभाषित रूपांतरण असाइनमेंट और प्रारंभिकरणों में उपयोग करने के लिए प्रतिबंधित नहीं हैं।

अवलोकन

स्पष्ट रूपांतरण फ़ंक्शंस और कन्स्ट्रक्टर का उपयोग केवल स्पष्ट रूपांतरण (प्रत्यक्ष प्रारंभिकरण या स्पष्ट कास्ट ऑपरेशन) के लिए किया जा सकता है, जबकि गैर-स्पष्ट कन्स्ट्रक्टर और रूपांतरण फ़ंक्शन का उपयोग अंतर्निहित और स्पष्ट रूपांतरणों के लिए किया जा सकता है।

/*
                                 explicit conversion          implicit conversion

 explicit constructor                    yes                          no

 constructor                             yes                          yes

 explicit conversion function            yes                          no

 conversion function                     yes                          yes

*/

संरचना X, Y, Z और फंक्शंस foo, bar, baz का उपयोग कर उदाहरण:

आइए explicit और गैर- explicit रूपांतरणों के बीच अंतर देखने के लिए संरचनाओं और कार्यों के एक छोटे से सेटअप को देखें।

struct Z { };

struct X { 
  explicit X(int a); // X can be constructed from int explicitly
  explicit operator Z (); // X can be converted to Z explicitly
};

struct Y{
  Y(int a); // int can be implicitly converted to Y
  operator Z (); // Y can be implicitly converted to Z
};

void foo(X x) { }
void bar(Y y) { }
void baz(Z z) { }

निर्माता के बारे में उदाहरण:

फ़ंक्शन तर्क का रूपांतरण:

foo(2);                     // error: no implicit conversion int to X possible
foo(X(2));                  // OK: direct initialization: explicit conversion
foo(static_cast<X>(2));     // OK: explicit conversion

bar(2);                     // OK: implicit conversion via Y(int) 
bar(Y(2));                  // OK: direct initialization
bar(static_cast<Y>(2));     // OK: explicit conversion

वस्तु प्रारंभिकरण:

X x2 = 2;                   // error: no implicit conversion int to X possible
X x3(2);                    // OK: direct initialization
X x4 = X(2);                // OK: direct initialization
X x5 = static_cast<X>(2);   // OK: explicit conversion 

Y y2 = 2;                   // OK: implicit conversion via Y(int)
Y y3(2);                    // OK: direct initialization
Y y4 = Y(2);                // OK: direct initialization
Y y5 = static_cast<Y>(2);   // OK: explicit conversion

रूपांतरण कार्यों के बारे में उदाहरण:

X x1{ 0 };
Y y1{ 0 };

फ़ंक्शन तर्क का रूपांतरण:

baz(x1);                    // error: X not implicitly convertible to Z
baz(Z(x1));                 // OK: explicit initialization
baz(static_cast<Z>(x1));    // OK: explicit conversion

baz(y1);                    // OK: implicit conversion via Y::operator Z()
baz(Z(y1));                 // OK: direct initialization
baz(static_cast<Z>(y1));    // OK: explicit conversion

वस्तु प्रारंभिकरण:

Z z1 = x1;                  // error: X not implicitly convertible to Z
Z z2(x1);                   // OK: explicit initialization
Z z3 = Z(x1);               // OK: explicit initialization
Z z4 = static_cast<Z>(x1);  // OK: explicit conversion

Z z1 = y1;                  // OK: implicit conversion via Y::operator Z()
Z z2(y1);                   // OK: direct initialization
Z z3 = Z(y1);               // OK: direct initialization
Z z4 = static_cast<Z>(y1);  // OK: explicit conversion

explicit रूपांतरण फ़ंक्शंस या कन्स्ट्रक्टर का उपयोग क्यों करें?

रूपांतरण कन्स्ट्रक्टर और गैर-स्पष्ट रूपांतरण फ़ंक्शन अस्पष्टता का परिचय दे सकते हैं।

एक संरचना V पर विचार करें, int परिवर्तनीय, संरचना U स्पष्ट रूप से रचनात्मक और क्रमशः U और bool लिए अधिभारित एक फ़ंक्शन f

struct V {
  operator bool() const { return true; }
};

struct U { U(V) { } };

void f(U) { }
void f(bool) {  }

टाइप V ऑब्जेक्ट को पास करते समय f को कॉल अस्पष्ट है।

V x;
f(x);  // error: call of overloaded 'f(V&)' is ambiguous

V ऑब्जेक्ट को परिवर्तित करने के लिए V ऑब्जेक्ट को कन्वर्ट करने के लिए कंपाइलर को U या कनवर्ज़न फ़ंक्शन के कन्स्ट्रक्टर का उपयोग करने के लिए गीलेर नहीं पता है।

यदि U के कन्स्ट्रक्टर या V के रूपांतरण समारोह explicit , तो कोई अस्पष्टता नहीं होगी क्योंकि केवल गैर-स्पष्ट रूपांतरण पर विचार किया जाएगा। यदि दोनों स्पष्ट हैं कि टाइप V किसी ऑब्जेक्ट का उपयोग करके f को कॉल करने के लिए एक स्पष्ट रूपांतरण या कास्ट ऑपरेशन का उपयोग करना होगा।

रूपांतरण कन्स्ट्रक्टर और गैर-स्पष्ट रूपांतरण फ़ंक्शन अप्रत्याशित व्यवहार का कारण बन सकते हैं।

कुछ वेक्टर मुद्रण समारोह पर विचार करें:

void print_intvector(std::vector<int> const &v) { for (int x : v) std::cout << x << '\n'; }

यदि वेक्टर का आकार-निर्माता स्पष्ट नहीं होगा तो इस तरह के फ़ंक्शन को कॉल करना संभव होगा:

print_intvector(3);

इस तरह की कॉल से कोई क्या उम्मीद करेगा? एक पंक्ति जिसमें 3 या तीन पंक्तियां हैं 0 ? (जहां दूसरा दूसरा होता है।)

एक वर्ग इंटरफ़ेस में स्पष्ट कीवर्ड का उपयोग करने से इंटरफ़ेस के उपयोगकर्ता को वांछित रूपांतरण के बारे में स्पष्ट होना पड़ता है।

जैसा कि बजेर्न स्ट्राउस्ट्रप इसे कहते हैं ("सी ++ प्रोग्रामिंग भाषा", चौथा एड।, 35.2.1, पीपी 1011) में सवाल है कि क्यों std::duration को सादे संख्या से स्पष्ट रूप से निर्मित नहीं किया जा सकता है:

यदि आप जानते हैं कि आपका क्या मतलब है, तो इसके बारे में स्पष्ट रहें।


यह उत्तर एक स्पष्ट कन्स्ट्रक्टर के साथ / बिना ऑब्जेक्ट सृजन के बारे में है क्योंकि यह अन्य उत्तरों में शामिल नहीं है।

एक स्पष्ट निर्माता के बिना निम्न वर्ग पर विचार करें:

class Foo
{
public:
    Foo(int x) : m_x(x)
    {
    }

private:
    int m_x;
};

कक्षा फू के ऑब्जेक्ट्स 2 तरीकों से बनाया जा सकता है:

Foo bar1(10);

Foo bar2 = 20;

कार्यान्वयन के आधार पर, कक्षा फू को तत्काल करने का दूसरा तरीका भ्रमित हो सकता है, या प्रोग्रामर का इरादा नहीं है। कन्स्ट्रक्टर को explicit कीवर्ड उपसर्ग करना Foo bar2 = 20; पर एक कंपाइलर त्रुटि उत्पन्न करेगा Foo bar2 = 20;

एकल-तर्क रचनाकारों को explicit रूप से घोषित करना आमतौर पर अच्छा अभ्यास होता है, जब तक कि आपका कार्यान्वयन विशेष रूप से प्रतिबंधित न हो।

ध्यान दें कि साथ रचनाकार

  • सभी मानकों के लिए डिफ़ॉल्ट तर्क, या
  • दूसरे पैरामीटर के लिए डिफ़ॉल्ट तर्क

दोनों एकल-तर्क कन्स्ट्रक्टर के रूप में इस्तेमाल किया जा सकता है। तो आप इन्हें भी explicit करना चाहते हैं।

एक उदाहरण जब आप जानबूझकर अपने सिंगल-तर्क कन्स्ट्रक्टर को स्पष्ट नहीं करना चाहते हैं तो यह है कि यदि आप एक मजेदार बना रहे हैं ( this उत्तर में घोषित 'add_x' स्ट्रक्चर देखें)। ऐसे मामले में, add_x add30 = 30; रूप में ऑब्जेक्ट बनाना add_x add30 = 30; शायद समझ में आता है।

Here स्पष्ट रचनाकारों पर एक अच्छा लेखन है।


रचनाकार निहित रूपांतरण संलग्न करते हैं। इस निहित रूपांतरण को दबाने के लिए इसे एक पैरामीटर स्पष्ट के साथ एक कन्स्ट्रक्टर घोषित करने की आवश्यकता है।

सी ++ 11 में आप ऐसे कीवर्ड के साथ "ऑपरेटर प्रकार ()" भी निर्दिष्ट कर सकते हैं here इस तरह के विनिर्देश के साथ आप स्पष्ट रूपांतरणों के संदर्भ में ऑपरेटर का उपयोग कर सकते हैं, और वस्तु का प्रत्यक्ष प्रारंभिकरण।

पीएस उपयोगकर्ता द्वारा परिभाषित परिवर्तनों का उपयोग करते समय (कन्स्ट्रक्टर और प्रकार रूपांतरण ऑपरेटर के माध्यम से) इसे केवल एक स्तर के निहित रूपांतरणों की अनुमति है। लेकिन आप इस रूपांतरण को अन्य भाषा रूपांतरणों के साथ जोड़ सकते हैं

  • अप अभिन्न रैंक (int से int, डबल करने के लिए तैरना);
  • स्टैंडअर्ट रूपांतरण (डबल करने के लिए int);
  • वस्तुओं के पॉइंटर्स को बेस क्लास में परिवर्तित करें और शून्य *;

कंपाइलर को फ़ंक्शन में पैरामीटर को हल करने के लिए एक अंतर्निहित रूपांतरण करने की अनुमति है। इसका अर्थ यह है कि संकलक पैरामीटर के लिए सही प्रकार प्राप्त करने के लिए एक प्रकार से दूसरे में परिवर्तित करने के लिए एकल पैरामीटर के साथ कॉल करने योग्य रचनाकारों का उपयोग कर सकता है।

यहां एक निर्माता के साथ एक उदाहरण वर्ग है जिसका उपयोग निहित रूपांतरणों के लिए किया जा सकता है:

class Foo
{
public:
  // single parameter constructor, can be used as an implicit conversion
  Foo (int foo) : m_foo (foo) 
  {
  }

  int GetFoo () { return m_foo; }

private:
  int m_foo;
};

यहां एक साधारण फ़ंक्शन है जो Foo ऑब्जेक्ट लेता है:

void DoBar (Foo foo)
{
  int i = foo.GetFoo ();
}

और यहां वह जगह है जहां DoBar फ़ंक्शन कहा जाता है।

int main ()
{
  DoBar (42);
}

तर्क एक Foo ऑब्जेक्ट नहीं है, लेकिन एक int । हालांकि, Foo लिए एक कन्स्ट्रक्टर मौजूद है जो एक int लेता है ताकि पैरामीटर को सही प्रकार में बदलने के लिए इस कन्स्ट्रक्टर का उपयोग किया जा सके।

प्रत्येक पैरामीटर के लिए संकलक को एक बार ऐसा करने की अनुमति है।

कन्स्ट्रक्टर को explicit कीवर्ड को उपसर्ग करना संकलक को अंतर्निहित रूपांतरणों के लिए उस कन्स्ट्रक्टर का उपयोग करने से रोकता है। उपर्युक्त वर्ग में इसे जोड़ने से फ़ंक्शन कॉल DoBar (42) पर एक कंपाइलर त्रुटि उत्पन्न होगी। अब DoBar (Foo (42)) साथ स्पष्ट रूप से रूपांतरण के लिए कॉल करना आवश्यक है

कारण आप ऐसा करना चाहते हैं कि आकस्मिक निर्माण से बचें जो कि बग को छुपा सकता है। योगदान उदाहरण:

  • आपके पास एक निर्माता के साथ एक MyString(int size) वर्ग है जो दिए गए आकार की एक स्ट्रिंग MyString(int size) । आपके पास एक फ़ंक्शन print(const MyString&) , और आप print(3) (जब आप वास्तव में print("3") कॉल करना चाहते थे)। आप इसे "3" प्रिंट करने की उम्मीद करते हैं, लेकिन यह इसके बजाय लंबाई 3 की खाली स्ट्रिंग प्रिंट करता है।

explicit कीवर्ड गैर-रूपांतरण कन्स्ट्रक्टर के लिए एक रूपांतरण कन्स्ट्रक्टर बनाता है। नतीजतन, कोड कम त्रुटि प्रवण है।


मान लीजिए, आपके पास एक वर्ग String :

class String {
public:
    String(int n); // allocate n bytes to the String object
    String(const char *p); // initializes object with char *p
};

अब, यदि आप कोशिश करते हैं:

String mystring = 'x';

चरित्र 'x' को अंतर्निहित रूप से int परिवर्तित किया जाएगा और फिर String(int) कन्स्ट्रक्टर को बुलाया जाएगा। लेकिन, ऐसा नहीं है कि उपयोगकर्ता का इरादा हो सकता है। इसलिए, ऐसी स्थितियों को रोकने के लिए, हम निर्माता को explicit रूप से परिभाषित करेंगे:

class String {
public:
    explicit String (int n); //allocate n bytes
    String(const char *p); // initialize sobject with string p
};




explicit-constructor