मुझे समझ नहीं आता कि मैं ऐसा क्यों करूंगा:
struct S {
int a;
S(int aa) : a(aa) {}
S() = default;
};
क्यों न कहें:
S() {} // instead of S() = default;
उसके लिए एक नया वाक्यविन्यास क्यों लाया जाए?
मुझे समझ नहीं आता कि मैं ऐसा क्यों करूंगा:
struct S {
int a;
S(int aa) : a(aa) {}
S() = default;
};
क्यों न कहें:
S() {} // instead of S() = default;
उसके लिए एक नया वाक्यविन्यास क्यों लाया जाए?
जवाबों:
एक डिफ़ॉल्ट डिफॉल्ट कंस्ट्रक्टर को विशेष रूप से एक यूज़र-डिफ़ाइंड डिफॉल्ट कंस्ट्रक्टर के रूप में परिभाषित किया जाता है जिसमें कोई आरंभीकरण सूची और एक खाली यौगिक स्टेटमेंट नहीं है।
§12.1 / 6 [class.ctor] एक डिफ़ॉल्ट कंस्ट्रक्टर जिसे डिफ़ॉल्ट रूप से परिभाषित किया गया है और हटाए जाने के रूप में परिभाषित नहीं किया गया है, यह स्पष्ट रूप से परिभाषित किया गया है कि जब यह अपने वर्ग प्रकार की वस्तु बनाने के लिए ओड-उपयोग किया जाता है या जब इसकी पहली घोषणा के बाद इसे स्पष्ट रूप से डिफ़ॉल्ट किया जाता है। अंतर्निहित रूप से परिभाषित डिफ़ॉल्ट निर्माणकर्ता उस वर्ग के आरंभीकरण का सेट करता है जो उस वर्ग के लिए उपयोगकर्ता-लिखित डिफ़ॉल्ट निर्माता द्वारा किया जाएगा जिसमें कोई ctor-initializer (12.6.2) और एक खाली यौगिक-कथन नहीं है। [...]
हालांकि, जबकि दोनों कंस्ट्रक्टर एक समान व्यवहार करेंगे, खाली कार्यान्वयन प्रदान करने से वर्ग के कुछ गुण प्रभावित होते हैं। उपयोगकर्ता द्वारा परिभाषित कंस्ट्रक्टर देते हुए, भले ही वह कुछ भी न करता हो, प्रकार को समुच्चय नहीं बनाता है और तुच्छ भी नहीं बनाता है । यदि आप चाहते हैं कि आपकी कक्षा एक समुच्चय या एक तुच्छ प्रकार (या सकारात्मकता, एक पीओडी प्रकार) हो, तो आपको उपयोग करने की आवश्यकता है = default।
§8.5.1 / 1 [dcl.init.aggr] एक समुच्चय एक सरणी या एक वर्ग है जिसमें कोई उपयोगकर्ता-प्रदान किए गए निर्माता नहीं हैं, [और ...]
§12.1 / 5 [class.ctor] एक डिफ़ॉल्ट कंस्ट्रक्टर तुच्छ है यदि यह उपयोगकर्ता द्वारा प्रदान नहीं किया गया है और [...]
§9 / 6 [वर्ग] एक तुच्छ वर्ग एक ऐसा वर्ग है जिसमें एक तुच्छ डिफ़ॉल्ट रचनाकार होता है और [...]
प्रदर्शित करना:
#include <type_traits>
struct X {
X() = default;
};
struct Y {
Y() { };
};
int main() {
static_assert(std::is_trivial<X>::value, "X should be trivial");
static_assert(std::is_pod<X>::value, "X should be POD");
static_assert(!std::is_trivial<Y>::value, "Y should not be trivial");
static_assert(!std::is_pod<Y>::value, "Y should not be POD");
}
इसके अतिरिक्त, स्पष्ट रूप से एक कंस्ट्रक्टर को डिफ़ॉल्ट करना यह बना देगा constexprयदि निहित निर्माणकर्ता होता है और यह भी एक ही अपवाद विनिर्देश देगा जो अंतर्निहित निर्माता के पास होगा। आपके द्वारा दिए गए मामले में, निहित निर्माणकर्ता नहीं होगा constexpr(क्योंकि यह एक डेटा सदस्य को निर्विवाद रूप से छोड़ देगा) और इसमें एक खाली अपवाद विनिर्देश भी होगा, इसलिए कोई अंतर नहीं है। लेकिन हां, सामान्य मामले में आप मैन्युअल रूप से निर्दिष्ट कर सकते हैं constexprऔर निहित विनिर्देशन से मेल खाने के लिए अपवाद विनिर्देश।
उपयोग करने से = defaultकुछ एकरूपता आती है, क्योंकि इसका उपयोग प्रतिलिपि / चाल निर्माणकर्ताओं और विध्वंसक के साथ भी किया जा सकता है। एक खाली कॉपी कंस्ट्रक्टर, उदाहरण के लिए, एक डिफॉल्ट कॉपी कंस्ट्रक्टर (जो अपने सदस्यों की सदस्य-वार कॉपी का प्रदर्शन करेगा) के समान नहीं करेगा। इनमें से प्रत्येक विशेष सदस्य फ़ंक्शन के लिए समान रूप से = default(या = delete) सिंटैक्स का उपयोग करना आपके इरादे को स्पष्ट रूप से बताते हुए आपके कोड को पढ़ना आसान बनाता है।
constexprकंस्ट्रक्टर (7.1.5) की आवश्यकताओं को पूरा करेगा , तो अंतर्निहित डिफॉल्ट कंस्ट्रक्टर है constexpr।"
constexprकि निहित घोषणा होगी, (बी) यह अंतर्निहित रूप से समान माना जाता है अपवाद-विनिर्देश मानो इसे (15.4) अनुमानित रूप से घोषित किया गया था, ... "इस विशिष्ट मामले में कोई फर्क नहीं पड़ता है, लेकिन सामान्य तौर पर foo() = default;इसका थोड़ा फायदा होता है foo() {}।
constexpr(चूंकि एक डेटा सदस्य को गैर-विनिर्मित छोड़ दिया गया है) और इसके अपवाद विनिर्देश सभी अपवादों की अनुमति देता है। मैं वह साफ कर दूंगा।
constexpr(जिसके साथ आपने उल्लेख किया है कि यहां अंतर नहीं करना चाहिए): struct S1 { int m; S1() {} S1(int m) : m(m) {} }; struct S2 { int m; S2() = default; S2(int m) : m(m) {} }; constexpr S1 s1 {}; constexpr S2 s2 {};केवल s1एक त्रुटि देता है, नहीं s2। क्लैंग और जी ++ दोनों में।
मेरे पास एक उदाहरण है जो अंतर दिखाएगा:
#include <iostream>
using namespace std;
class A
{
public:
int x;
A(){}
};
class B
{
public:
int x;
B()=default;
};
int main()
{
int x = 5;
new(&x)A(); // Call for empty constructor, which does nothing
cout << x << endl;
new(&x)B; // Call for default constructor
cout << x << endl;
new(&x)B(); // Call for default constructor + Value initialization
cout << x << endl;
return 0;
}
आउटपुट:
5
5
0
जैसा कि हम देख सकते हैं कि खाली ए () कंस्ट्रक्टर के लिए कॉल सदस्यों को आरंभीकृत नहीं करता है, जबकि बी () करता है।
n2210 कुछ कारण प्रदान करता है:
चूक के प्रबंधन में कई समस्याएं हैं:
- कंस्ट्रक्टर परिभाषाएँ युग्मित हैं; किसी भी कंस्ट्रक्टर को घोषित करने से डिफ़ॉल्ट कंस्ट्रक्टर को दबा दिया जाता है।
- विध्वंसक डिफ़ॉल्ट पॉलीमॉर्फिक वर्गों के लिए अनुचित है, एक स्पष्ट परिभाषा की आवश्यकता होती है।
- एक बार एक डिफ़ॉल्ट दबा दिया जाता है, तो इसे फिर से जीवित करने का कोई साधन नहीं है।
- मैन्युअल रूप से निर्दिष्ट कार्यान्वयन की तुलना में डिफ़ॉल्ट कार्यान्वयन अक्सर अधिक कुशल होते हैं।
- गैर-डिफ़ॉल्ट कार्यान्वयन गैर-तुच्छ हैं, जो प्रकार शब्दार्थ को प्रभावित करता है, उदाहरण के लिए एक गैर-पीओडी बनाता है।
- (गैर-तुच्छ) विकल्प घोषित किए बिना विशेष सदस्य फ़ंक्शन या वैश्विक ऑपरेटर को प्रतिबंधित करने का कोई साधन नहीं है।
type::type() = default; type::type() { x = 3; }कुछ मामलों में, सदस्य फ़ंक्शन परिभाषा में बदलाव की आवश्यकता के बिना वर्ग निकाय बदल सकता है क्योंकि अतिरिक्त सदस्यों की घोषणा के साथ डिफ़ॉल्ट बदल जाता है।
देखें नियम-ऑफ-थ्री C ++ 11 के साथ नियम-पांच बन जाता है? :
ध्यान दें कि एक कंस्ट्रक्टर और मूव असाइनमेंट ऑपरेटर को एक ऐसे वर्ग के लिए उत्पन्न नहीं किया जाएगा जो स्पष्ट रूप से अन्य विशेष सदस्य कार्यों में से किसी को घोषित करता है, कि कॉपी कंस्ट्रक्टर और कॉपी असाइनमेंट ऑपरेटर को उस वर्ग के लिए उत्पन्न नहीं किया जाएगा जो स्पष्ट रूप से एक चाल निर्माता या चाल की घोषणा करता है असाइनमेंट ऑपरेटर, और यह कि एक वर्ग जो स्पष्ट रूप से घोषित विध्वंसक और स्पष्ट रूप से परिभाषित कॉपी निर्माता या अनुमानित रूप से परिभाषित कॉपी असाइनमेंट ऑपरेटर है, को पदावनत माना जाता है
= defaultसामान्य रूप से होने के कारण हैं , बजाय = defaultएक निर्माता बनाम करने के कारणों के { }।
{}पहले से ही की शुरूआत करने से पहले भाषा की एक विशेषता थी =default, इन कारणों से है भेद पर भरोसा करते हैं परोक्ष (जैसे संकेत मिलता है कि "वहाँ जीवित [एक दबा डिफ़ॉल्ट] के लिए किसी भी तरह है" {}है नहीं डिफ़ॉल्ट के बराबर )।
यह कुछ मामलों में शब्दार्थ की बात है। यह डिफ़ॉल्ट बिल्डरों के साथ बहुत स्पष्ट नहीं है, लेकिन यह अन्य संकलक-जनित सदस्य कार्यों के साथ स्पष्ट हो जाता है।
डिफॉल्ट कंस्ट्रक्टर के लिए, किसी भी डिफॉल्ट कंस्ट्रक्टर को संभव हो सकता है कि खाली बॉडी के साथ एक तुच्छ कंस्ट्रक्टर होने के लिए एक उम्मीदवार माना जाए, जैसे कि उपयोग करना =default। आखिरकार, पुराने खाली डिफ़ॉल्ट निर्माता कानूनी C ++ थे ।
struct S {
int a;
S() {} // legal C++
};
कंपाइलर इस निर्माता को तुच्छ समझता है या नहीं, यह अनुकूलन (मैनुअल या कंपाइलर वाले) के बाहर ज्यादातर मामलों में अप्रासंगिक है।
हालांकि, खाली फ़ंक्शन बॉडी को "डिफ़ॉल्ट" मानने का यह प्रयास अन्य प्रकार के सदस्य कार्यों के लिए पूरी तरह से टूट जाता है। प्रतिलिपि निर्माता पर विचार करें:
struct S {
int a;
S() {}
S(const S&) {} // legal, but semantically wrong
};
उपरोक्त मामले में, खाली बॉडी के साथ लिखा गया कॉपी कंस्ट्रक्टर अब गलत है । यह अब वास्तव में कुछ भी कॉपी नहीं कर रहा है। यह डिफ़ॉल्ट कॉपी कंस्ट्रक्टर शब्दार्थ की तुलना में शब्दार्थों का एक बहुत अलग सेट है। वांछित व्यवहार के लिए आपको कुछ कोड लिखना होगा:
struct S {
int a;
S() {}
S(const S& src) : a(src.a) {} // fixed
};
इस सरल मामले के साथ भी, हालांकि, यह संकलक के लिए यह सुनिश्चित करने के लिए अधिक बोझ बन रहा है कि कॉपी निर्माणकर्ता उसी के समान है जो वह स्वयं उत्पन्न करेगा या यह देखने के लिए कि कॉपी निर्माता तुच्छ है (एक के बराबर)memcpy मूल रूप से, इसके )। कंपाइलर को प्रत्येक सदस्य इनिशियलाइज़र एक्सप्रेशन की जाँच करनी होगी और यह सुनिश्चित करना होगा कि स्रोत के संबंधित सदस्य तक पहुँच के लिए यह एक्सप्रेशन के समान हो और कुछ भी न हो, यह सुनिश्चित करें कि कोई भी सदस्य नॉन-ट्रिवियल डिफॉल्ट कंस्ट्रक्शन आदि से बचे नहीं हैं, यह प्रक्रिया का एक तरीका है। संकलक यह सत्यापित करने के लिए उपयोग करेगा कि यह इस फ़ंक्शन के स्वयं के उत्पन्न संस्करण तुच्छ हैं।
फिर कॉपी असाइनमेंट ऑपरेटर पर विचार करें, जो बालों को भी प्राप्त कर सकता है, विशेष रूप से गैर-तुच्छ मामले में। यह बॉयलर-प्लेट का एक टन है जिसे आप कई वर्गों के लिए नहीं लिखना चाहते हैं, लेकिन आप सी ++ में किसी भी तरह से मजबूर हो सकते हैं: 03
struct T {
std::shared_ptr<int> b;
T(); // the usual definitions
T(const T&);
T& operator=(const T& src) {
if (this != &src) // not actually needed for this simple example
b = src.b; // non-trivial operation
return *this;
};
यह एक सरल मामला है, लेकिन यह पहले से ही अधिक कोड है जितना कि आप कभी भी इस तरह के एक सरल प्रकार के लिए लिखने के लिए मजबूर होना चाहते हैं T(विशेष रूप से एक बार जब हम मिश्रण में संचालन स्थानांतरित करते हैं)। हम एक खाली शरीर पर भरोसा नहीं कर सकते हैं जिसका अर्थ है "चूक में भरें" क्योंकि खाली शरीर पहले से ही पूरी तरह से वैध है और इसका स्पष्ट अर्थ है। वास्तव में, यदि खाली निकाय का उपयोग "डिफॉल्ट में भरने" को दर्शाने के लिए किया जाता था, तो स्पष्ट रूप से एक नो-ऑप कॉपी कंस्ट्रक्टर या जैसे बनाने का कोई तरीका नहीं होगा।
यह फिर से स्थिरता की बात है। खाली शरीर का अर्थ है "कुछ भी न करें" लेकिन कॉपी कंस्ट्रक्टर जैसी चीजों के लिए जो आप वास्तव में "कुछ नहीं करना चाहते हैं" बल्कि "उन सभी चीजों को करें जो आप सामान्य रूप से करेंगे अगर दबाए नहीं।" इसलिए =default। यह दबा हुआ कंपाइलर-जनरेट किए गए सदस्य फ़ंक्शंस जैसे कॉपी / मूव कन्स्ट्रक्टर्स और असाइनमेंट ऑपरेटर्स पर काबू पाने के लिए आवश्यक है । यह तब डिफ़ॉल्ट रूप से अच्छी तरह से काम करने के लिए "स्पष्ट" है।
हो सकता है कि खाली बॉडी के साथ डिफ़ॉल्ट कंस्ट्रक्टर बनाना अच्छा रहा हो और तुच्छ सदस्य / बेस कंस्ट्रक्टर को भी तुच्छ माना जाता है, जैसे कि उनके साथ होता है =defaultयदि पुराने कोड को कुछ मामलों में अधिक इष्टतम बनाने के लिए, लेकिन सबसे निम्न-स्तर का कोड तुच्छ पर निर्भर करता है अनुकूलन के लिए डिफ़ॉल्ट निर्माणकर्ता भी तुच्छ प्रतिलिपि निर्माणकर्ताओं पर निर्भर होते हैं। यदि आपको अपने सभी पुराने कॉपी कंस्ट्रक्टर्स के पास जाना है और उन्हें "ठीक" करना है, तो यह वास्तव में आपके सभी पुराने डिफॉल्ट कंस्ट्रक्टरों को ठीक करने के लिए बहुत अधिक खिंचाव नहीं है। यह =defaultआपके इरादों को निरूपित करने के लिए स्पष्ट का उपयोग करके बहुत अधिक स्पष्ट और अधिक स्पष्ट है ।
कुछ अन्य चीजें हैं जो संकलित-जनरेट किए गए सदस्य फ़ंक्शन करेंगे जो आपको स्पष्ट रूप से समर्थन में परिवर्तन करने के लिए, साथ ही साथ करना होगा। constexprडिफ़ॉल्ट बिल्डरों के लिए समर्थन एक उदाहरण है। यह केवल मानसिक रूप से उपयोग करने की =defaultतुलना में अन्य सभी विशेष कीवर्ड के साथ कार्यों को चिह्नित करने के लिए आसान है और इस तरह से निहित है =defaultऔर यह C ++ 11 के विषयों में से एक था: भाषा को आसान बनाते हैं। यह अभी भी मौसा और बैक-कमर्स के बहुत सारे समझौता करता है, लेकिन यह स्पष्ट है कि यह C ++ 03 से एक बड़ा कदम है जब यह आसानी से उपयोग में आता है।
= defaultहोगा a=0;और नहीं था! मुझे इसके पक्ष में छोड़ना पड़ा : a(0)। मैं अभी भी उलझन में हूं कि = defaultयह कितना उपयोगी है, क्या यह प्रदर्शन के बारे में है? अगर मैं सिर्फ उपयोग नहीं करता तो क्या यह कहीं टूट जाएगा = default? मैंने यहाँ सभी उत्तरों को पढ़ने की कोशिश की, मैं कुछ सी ++ सामानों के लिए नया हूँ और मुझे इसे समझने में बहुत परेशानी हो रही है।
a=0उदाहरण तुच्छ प्रकार के व्यवहार के कारण है, जो एक अलग (यद्यपि संबंधित) विषय हैं।
= defaultऔर अभी भी अनुदान aहोगा =0? किसी तरह? क्या आपको लगता है कि मैं एक नया प्रश्न बना सकता हूं जैसे "कैसे एक निर्माणकर्ता = defaultऔर अनुदान देने के लिए खेतों को ठीक से आरंभीकृत किया जाएगा?", btw मुझे एक structऔर नहीं में समस्या थी class, और ऐप सही तरीके से चल रहा है यहां तक कि उपयोग नहीं कर = defaultरहा है, मैं कर सकता हूं। उस सवाल पर एक न्यूनतम संरचना जोड़ें अगर यह एक अच्छा है :)
struct { int a = 0; };यदि आप तय करते हैं कि आपको एक निर्माता की आवश्यकता है, तो आप इसे डिफ़ॉल्ट कर सकते हैं, लेकिन ध्यान दें कि प्रकार तुच्छ नहीं होगा (जो ठीक है)।
की निंदा के कारण std::is_podवैकल्पिक और उसके std::is_trivial && std::is_standard_layout, @JosephMansfield के जवाब से स्निपेट हो जाता है:
#include <type_traits>
struct X {
X() = default;
};
struct Y {
Y() {}
};
int main() {
static_assert(std::is_trivial_v<X>, "X should be trivial");
static_assert(std::is_standard_layout_v<X>, "X should be standard layout");
static_assert(!std::is_trivial_v<Y>, "Y should not be trivial");
static_assert(std::is_standard_layout_v<Y>, "Y should be standard layout");
}
ध्यान दें कि Yयह अभी भी मानक लेआउट का है।
defaultकोई नया कीवर्ड नहीं है, यह पहले से आरक्षित कीवर्ड का नया उपयोग मात्र है।