c++ - स्पष्ट कीवर्ड का क्या अर्थ है?
constructor explicit (8)
सी ++ में explicit
कीवर्ड का क्या अर्थ है?
कीवर्ड 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
को सादे संख्या से स्पष्ट रूप से निर्मित नहीं किया जा सकता है:
यदि आप जानते हैं कि आपका क्या मतलब है, तो इसके बारे में स्पष्ट रहें।
इस पर पहले ही चर्चा की जा चुकी है ( स्पष्ट कन्स्ट्रक्टर क्या है )। लेकिन मुझे कहना होगा कि इसमें यहां दिए गए विस्तृत विवरणों की कमी है।
इसके अलावा, यह हमेशा एक अच्छा कोडिंग अभ्यास है जो आपके एक तर्क कन्स्ट्रक्टर (जिसमें Arg2, arg3, ... के लिए डिफ़ॉल्ट मान वाले हैं) शामिल हैं। सी ++ के साथ हमेशा की तरह: यदि आप नहीं करते हैं - तो आप चाहेंगे कि आपने किया ...
कक्षाओं के लिए एक और अच्छा अभ्यास कॉपी निर्माण और असाइनमेंट निजी (उर्फ इसे अक्षम करें) बनाना है जब तक कि आपको वास्तव में इसे लागू करने की आवश्यकता न हो। यह उन तरीकों का उपयोग करते समय पॉइंटर्स की अंतिम प्रतियां प्राप्त करता है जो डिफ़ॉल्ट रूप से C ++ आपके लिए बनाएंगे। ऐसा करने का एक और तरीका बूस्ट :: noncopyable से प्राप्त होता है।
मान लीजिए, आपके पास एक वर्ग 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
};
यह उत्तर एक स्पष्ट कन्स्ट्रक्टर के साथ / बिना ऑब्जेक्ट सृजन के बारे में है क्योंकि यह अन्य उत्तरों में शामिल नहीं है।
एक स्पष्ट निर्माता के बिना निम्न वर्ग पर विचार करें:
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 स्पष्ट रचनाकारों पर एक अच्छा लेखन है।
सी ++ में, केवल एक आवश्यक पैरामीटर वाला एक कन्स्ट्रक्टर एक अंतर्निहित रूपांतरण फ़ंक्शन माना जाता है। यह पैरामीटर प्रकार को कक्षा प्रकार में परिवर्तित करता है। चाहे यह एक अच्छी बात है या नहीं, कन्स्ट्रक्टर के अर्थशास्त्र पर निर्भर करता है।
उदाहरण के लिए, यदि आपके पास कन्स्ट्रक्टर String(const char* s)
साथ एक स्ट्रिंग क्लास है, तो शायद यह वही है जो आप चाहते हैं। आप String
अपेक्षा रखने वाले फ़ंक्शन में एक const char*
पास कर सकते हैं, और कंपाइलर स्वचालित रूप से आपके लिए एक अस्थायी String
ऑब्जेक्ट का निर्माण करेगा।
दूसरी तरफ, यदि आपके पास बफर क्लास है जिसका कन्स्ट्रक्टर Buffer(int size)
बाइट्स में बफर का आकार लेता है, तो शायद आप नहीं चाहते कि संकलक चुपचाप Buffer
एस में Buffer
। इसे रोकने के लिए, आप explicit
कीवर्ड के साथ निर्माता को घोषित करते हैं:
class Buffer { explicit Buffer(int size); ... }
उस तरफ,
void useBuffer(Buffer& buf);
useBuffer(4);
संकलन-समय त्रुटि बन जाती है। यदि आप अस्थायी Buffer
ऑब्जेक्ट को पास करना चाहते हैं, तो आपको स्पष्ट रूप से ऐसा करना होगा:
useBuffer(Buffer(4));
संक्षेप में, यदि आपका एकल-पैरामीटर कन्स्ट्रक्टर पैरामीटर को आपकी कक्षा के किसी ऑब्जेक्ट में परिवर्तित करता है, तो शायद आप explicit
कीवर्ड का उपयोग नहीं करना चाहते हैं। लेकिन अगर आपके पास एक कन्स्ट्रक्टर है जो केवल एक पैरामीटर लेने के लिए होता है, तो आपको संकलक को अप्रत्याशित रूपांतरणों से आश्चर्यचकित करने से रोकने के लिए इसे explicit
रूप से घोषित करना चाहिए।
सीपीपी संदर्भ हमेशा मददगार है !!! स्पष्ट विनिर्देशक के बारे में विवरण here पाया जा सकता here । आपको निहित रूपांतरण और copy-initialization भी देखने की आवश्यकता हो सकती है।
त्वरित देखो
स्पष्ट विनिर्देशक निर्दिष्ट करता है कि एक कन्स्ट्रक्टर या रूपांतरण फ़ंक्शन (सी ++ 11 के बाद) अंतर्निहित रूपांतरण या प्रति-प्रारंभिकरण की अनुमति नहीं देता है।
उदाहरण निम्नानुसार है:
struct A
{
A(int) { } // converting constructor
A(int, int) { } // converting constructor (C++11)
operator bool() const { return true; }
};
struct B
{
explicit B(int) { }
explicit B(int, int) { }
explicit operator bool() const { return true; }
};
int main()
{
A a1 = 1; // OK: copy-initialization selects A::A(int)
A a2(2); // OK: direct-initialization selects A::A(int)
A a3 {4, 5}; // OK: direct-list-initialization selects A::A(int, int)
A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
A a5 = (A)1; // OK: explicit cast performs static_cast
if (a1) cout << "true" << endl; // OK: A::operator bool()
bool na1 = a1; // OK: copy-initialization selects A::operator bool()
bool na2 = static_cast<bool>(a1); // OK: static_cast performs direct-initialization
// B b1 = 1; // error: copy-initialization does not consider B::B(int)
B b2(2); // OK: direct-initialization selects B::B(int)
B b3 {4, 5}; // OK: direct-list-initialization selects B::B(int, int)
// B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int)
B b5 = (B)1; // OK: explicit cast performs static_cast
if (b5) cout << "true" << endl; // OK: B::operator bool()
// bool nb1 = b2; // error: copy-initialization does not consider B::operator bool()
bool nb2 = static_cast<bool>(b2); // OK: static_cast performs direct-initialization
}
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 में मानकीकृत किया गया है।
स्पष्ट रूपांतरण कन्स्ट्रक्टर (केवल सी ++)
स्पष्ट फ़ंक्शन विनिर्देशक अवांछित अंतर्निहित प्रकार रूपांतरणों को नियंत्रित करता है। इसका उपयोग केवल वर्ग घोषणा के भीतर रचनाकारों की घोषणाओं में किया जा सकता है। उदाहरण के लिए, डिफ़ॉल्ट कन्स्ट्रक्टर को छोड़कर, निम्न श्रेणी में निर्माता रूपांतरण कन्स्ट्रक्टर हैं।
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);