C99 के रूप में निर्दिष्ट प्रारंभिक सूचियों का समर्थन C ++ 11 क्यों नहीं करता है? [बन्द है]


121

विचार करें:

struct Person
{
    int height;
    int weight;
    int age;
};

int main()
{
    Person p { .age = 18 };
}

उपरोक्त कोड C99 में कानूनी है, लेकिन C ++ 11 में कानूनी नहीं है।

क्या था ऐसी आसान सुविधा के लिए समर्थन को छोड़कर मानक समिति का औचित्य?


10
यह स्पष्ट रूप से इसे शामिल करने के लिए डिजाइन समिति के लिए कोई मतलब नहीं था, या यह बस बैठकों में नहीं आया था। यह ध्यान देने योग्य है कि C99 नामित इनिशियलाइज़र C ++ विनिर्देश संस्करणों में से किसी में नहीं हैं । कंस्ट्रक्टर्स को पसंदीदा इनिशियलाइज़ेशन कंस्ट्रक्शन लगता है, और अच्छे कारण के लिए: वे लगातार ऑब्जेक्ट इनिशियलाइज़ेशन की गारंटी देते हैं, यदि आप उन्हें सही तरीके से लिखते हैं।
रॉबर्ट हार्वे

19
आपका तर्क पिछड़ा हुआ है, किसी भाषा को फीचर न होने के लिए औचित्य की आवश्यकता नहीं है , उस पर एक और एक मजबूत होने के लिए एक तर्क की आवश्यकता है। C ++ पर्याप्त रूप से फूला हुआ है, क्योंकि यह खड़ा है।
मैथ्यू एम।

42
एक अच्छा कारण (जिसे बेवकूफ़ बनाने वाले रैपर लिखने के अलावा निर्माणकर्ताओं के साथ हल नहीं किया जा सकता है) यह है कि आप सी ++ का उपयोग करते हैं या नहीं, अधिकांश वास्तविक एपीआई सी हैं, सी ++ नहीं हैं, और उनमें से कुछ भी आपको एक संरचना की आपूर्ति नहीं करते हैं जिसमें आप सेट करना चाहते हैं। एक या दो क्षेत्र - और जरूरी नहीं कि पहला - लेकिन बाकी शून्य-प्रारंभिक होने की आवश्यकता है। Win32 API OVERLAPPEDएक ऐसा उदाहरण है। लिखने में सक्षम होने से ={.Offset=12345};कोड बहुत स्पष्ट हो जाएगा (और शायद कम त्रुटि-प्रवण)। बीएसडी सॉकेट एक समान उदाहरण हैं।
डेमन

14
कोड में mainC99 कानूनी नहीं है। इसे पढ़ना चाहिए struct Person p = { .age = 18 };
चकरली

14
FYI C ++ 20 नामित
शुरुआती का

जवाबों:


34

C ++ में कंस्ट्रक्टर हैं। यदि यह सिर्फ एक सदस्य को शुरू करने के लिए समझ में आता है, तो एक उचित निर्माणकर्ता को लागू करके कार्यक्रम में व्यक्त किया जा सकता है। यह अमूर्त C ++ को बढ़ावा देने का एक प्रकार है।

दूसरी ओर निर्दिष्ट शुरुआती सुविधा को उजागर करने और सदस्यों को क्लाइंट कोड में सीधे पहुंचने में आसान बनाने के बारे में अधिक है। यह 18 वर्ष (वर्ष?) के व्यक्ति की तरह चीजों को जन्म देता है, लेकिन ऊंचाई और शून्य के वजन के साथ।


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

C ++ एक प्रकार के डिज़ाइनर के बजाय लचीलेपन को रखने में अधिक रुचि रखता है , इसलिए डिज़ाइनर सही प्रकार का उपयोग करना आसान बना सकते हैं और गलत तरीके से उपयोग करना मुश्किल है। डिजाइनर को इस बात के नियंत्रण में रखना कि किसी प्रकार को कैसे इनिशियलाइज़ किया जा सकता है, इस का एक हिस्सा है: डिजाइनर कंस्ट्रक्टर, इन-क्लास एडवाइज़र आदि को निर्धारित करता है।


12
कृपया जो आप कहते हैं, उसके लिए एक संदर्भ लिंक सी ++ के लिए निर्दिष्ट आरंभीकरण न होने का कारण बताएं। मुझे याद नहीं है कि कभी इसके लिए प्रस्ताव देखा गया हो।
जोहान्स शहाब -

20
क्या Personइसके लिए एक निर्माता प्रदान नहीं करने का बहुत कारण नहीं है कि इसका लेखक उपयोगकर्ताओं को सदस्यों को सेट करने और आरंभ करने के लिए सबसे अधिक संभव लचीलापन प्रदान करना चाहता था? उपयोगकर्ता पहले से ही लिख सकता है Person p = { 0, 0, 18 };(और अच्छे कारणों के लिए)।
जोहान्स शहाब -

7
कुछ इसी तरह हाल ही में खुले ++ std.org/jtc1/sc22/wg21/docs/papers/2013/n3605.html द्वारा सी ++ 14 कल्पना में स्वीकार किया गया है ।
जोहान्स शहाब -

4
@ जोहान्सचैब-लिटब मैं विशुद्ध रूप से यांत्रिक, समीपस्थ कारण के बारे में बात नहीं कर रहा हूं (यानी, यह समिति के लिए प्रस्तावित नहीं किया गया है)। मैं वर्णन कर रहा हूं कि मैं जो मानता हूं वह हावी कारक है। - Personएक बहुत ही सी डिजाइन है इसलिए सी सुविधाओं का मतलब हो सकता है। हालाँकि C ++ शायद एक बेहतर डिज़ाइन को सक्षम बनाता है जो नामित शुरुआती के लिए आवश्यकता को भी कम करता है। - मेरे विचार में एग्रीगेट्स के लिए इन-क्लास इनिशियलाइज़र्स पर प्रतिबंध को हटा देना, सीरिज़ के निर्दिष्ट एनालाइज़र की तुलना में C ++ के लोकाचार के अनुरूप है।
bames53

4
इसके लिए C ++ प्रतिस्थापन को फ़ंक्शन तर्कों का नाम दिया जा सकता है। लेकिन अभी तक, आधिकारिक तौर पर नाम के तर्क मौजूद नहीं हैं। इसके प्रस्ताव के लिए N4172 नामांकित तर्क देखें । यह कोड कम त्रुटि प्रवण और पढ़ने में आसान बना देगा।
डेविड बेयर्ड

89

15 जुलाई को '17 P0329R4 को स्वीकार किया गयामानक: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf
यह सीमित समर्थन लाता हैआरंभिक पदनाम। इस सीमा को C.1.7 द्वारा इस प्रकार वर्णित किया गया है [diff.decl] .4, दिया गया:

struct A { int x, y; };
struct B { struct A a; };

सी में मान्य निम्नलिखित निर्दिष्ट प्रारंभियाँ C ++ में प्रतिबंधित हैं:

  • struct A a = { .y = 1, .x = 2 } C ++ में अमान्य है क्योंकि डेटा सदस्यों को डेटा सदस्यों के घोषणा क्रम में दिखाई देना चाहिए
  • int arr[3] = { [1] = 5 } C ++ में अमान्य है क्योंकि सरणी निर्दिष्ट आरंभीकरण समर्थित नहीं है
  • struct B b = {.a.x = 0} C ++ में अमान्य है क्योंकि डिज़ाइनर नेस्टेड नहीं हो सकते
  • struct A c = {.x = 1, 2} C ++ में अमान्य है क्योंकि सभी या डेटा सदस्यों में से कोई भी पदनामकों द्वारा आरंभीकृत नहीं किया जाना चाहिए

के लिये और पहले बूस्ट में वास्तव में नामित इंटेलाइज़र्स के लिए समर्थन है और समर्थन को जोड़ने के लिए कई प्रस्ताव आए हैंमानक, उदाहरण के लिए: n4172 और प्रारंभिक करने के लिए पदनाम वॉकर के प्रस्ताव को जोड़ने का प्रस्ताव । प्रस्तावों के कार्यान्वयन का हवाला देते हैंविज़ुअल C ++ में शुरुआती डिज़ाइनर, gcc और Clang का दावा:

हमारा मानना ​​है कि बदलाव लागू करने के लिए अपेक्षाकृत सरल होंगे

लेकिन मानक समिति बार-बार ऐसे प्रस्तावों को खारिज करती है , जिसमें कहा गया है:

EWG ने प्रस्तावित दृष्टिकोण के साथ विभिन्न समस्याएं पाईं, और यह नहीं सोचा कि समस्या को हल करने की कोशिश करना संभव है, क्योंकि इसे कई बार आजमाया गया है और हर बार यह विफल रहा है

बेन वोइगट की टिप्पणियों ने मुझे इस दृष्टिकोण के साथ दुर्गम समस्याओं को देखने में मदद की; दिया हुआ:

struct X {
    int c;
    char a;
    float b;
};

इन कार्यों को किस क्रम में बुलाया जाएगा? : struct X foo = {.a = (char)f(), .b = g(), .c = h()}? हैरानी की बात है, में:

किसी भी आरंभक में उपमेय के मूल्यांकन का क्रम अनिश्चित रूप से अनुक्रमित है [ 1 ]

(विजुअल C ++, gcc , और Clang में व्यवहार पर सहमति व्यक्त की गई है क्योंकि वे सभी इस क्रम में कॉल करेंगे :)

  1. h()
  2. f()
  3. g()

लेकिन मानक की अनिश्चित प्रकृति का मतलब है कि अगर इन कार्यों में कोई भी परस्पर क्रिया होती है तो परिणामी कार्यक्रम स्थिति भी अनिश्चित होगी, और संकलक आपको चेतावनी नहीं देगा : क्या दुर्व्यवहार के बारे में चेतावनी प्राप्त करने का एक तरीका है डिजाइन किए गए शुरुआती?

करता कड़े प्रारंभकर्ता-सूची आवश्यकताओं है 11.6.4 [dcl.init.list] 4:

ब्रैड-इन-लिस्ट के इनिशियलाइज़र-लिस्ट के भीतर, इनिशियलाइज़र-क्लॉज़, जिसमें पैक विस्तार (17.5.3) से कोई भी परिणाम शामिल होता है, का मूल्यांकन उस क्रम में किया जाता है जिसमें वे दिखाई देते हैं। यही है, किसी दिए गए इनिशलाइज़र-क्लॉज से जुड़े हर वैल्यू कंप्यूटेशन और साइड ई with एक्ट को किसी भी इनिशिएटिव-क्लॉज से जुड़े हर वैल्यू कंप्यूटेशन और साइड इफेक्ट से पहले सीक्वेंस किया जाता है, जो इसे इनिशियलाइज़र-लिस्ट के कॉमा सेपरेटेड लिस्ट में फॉलो करता है।

इसलिए समर्थन के लिए इस आदेश में निष्पादित होना आवश्यक है:

  1. f()
  2. g()
  3. h()

पिछले के साथ संगतता तोड़ कार्यान्वयन।
जैसा कि ऊपर चर्चा की गई है, इस मुद्दे को डिजाइन किए गए प्रारंभिकों पर सीमाओं को दरकिनार कर दिया गया है। वे एक मानकीकृत व्यवहार प्रदान करते हैं, नामित शुरुआती के निष्पादन के आदेश की गारंटी देते हैं।


3
इस कोड में ज़रूर: struct X { int c; char a; float b; }; X x = { .a = f(), .b = g(), .c = h() };कॉल करने के h()लिए f()या तो पहले किया जाता है g()। अगर पास की परिभाषा struct Xपास नहीं है, तो यह बहुत ही आश्चर्यजनक होने वाला है। याद रखें कि शुरुआती अभिव्यक्तियों को साइड-इफ़ेक्ट फ़्री होना ज़रूरी नहीं है।
बेन वोइगट

2
बेशक, यह कोई नई बात नहीं है, ctor मेंबर इनिशियलाइज़ेशन में पहले से ही यह समस्या है, लेकिन यह एक क्लास मेंबर की परिभाषा में है, इसलिए टाइट कपलिंग कोई आश्चर्य की बात नहीं है। और निर्दिष्ट आरंभकर्ता अन्य सदस्यों को संदर्भित नहीं कर सकते हैं जिस तरह से ctor सदस्य- initializers कर सकते हैं।
बेन वोइगट

2
@MattMcNabb: नहीं, यह अधिक चरम नहीं है। लेकिन एक उम्मीद करता है कि डेवलपर डेवलपर को सदस्य घोषणा आदेश को जानने के लिए वर्ग निर्माणकर्ता को लागू करेगा। जबकि पूरी तरह से वर्ग का उपभोक्ता एक अलग प्रोग्रामर हो सकता है। चूंकि पूरे बिंदु सदस्यों के आदेश को देखने के लिए बिना अनुमति के प्रारंभ करने के लिए है, इसलिए यह प्रस्ताव में एक घातक दोष की तरह लगता है। चूँकि निर्दिष्ट इनिशियलाइज़र्स निर्माण की जा रही वस्तु का संदर्भ नहीं दे सकते हैं, इसलिए पहली धारणा यह है कि आरम्भिक अभिव्यक्तियों का मूल्यांकन सबसे पहले किया जा सकता है, पदनाम क्रम में, फिर सदस्य इनिशियलाइज़ेशन डिक्लेरेशन ऑर्डर में। लेकिन ...
बेन Voigt

2
@JonathanMee: ठीक है, दूसरे प्रश्न का उत्तर दिया गया कि ... C99 कुल संधारित्र अनियंत्रित हैं, इसलिए निर्दिष्ट आरंभिक के लिए कोई उम्मीद नहीं है। C ++ लट-इन-इन-सूचियों का आदेश दिया जाता है, और नामित शुरुआती के लिए प्रस्ताव एक संभावित-आश्चर्यजनक आदेश का उपयोग करता है (आप शाब्दिक-इनिट सूचियों के लिए उपयोग किए जाने वाले, और ctor-initializer के लिए उपयोग किए जाने वाले सदस्य क्रम, दोनों के साथ संगत नहीं हो सकते हैं) -लिस्ट्स)
Ben Voigt

3
जोनाथन: "c ++ सपोर्ट के लिए इसे क्रम में निष्पादित करने की आवश्यकता होगी [...] पिछले c99 कार्यान्वयन के साथ संगतता को तोड़ते हुए।" मुझे यह नहीं मिलता, क्षमा करें। 1. यदि आदेश C99 में अनिश्चित है, तो जाहिर है कि कोई भी वास्तविक आदेश ठीक होना चाहिए, जिसमें कोई भी मनमाना C ++ विकल्प शामिल है। b) देस का समर्थन नहीं। शुरुआती सभी तरह के शुरुआती पहले से ही C99 संगतता को और भी अधिक तोड़ देता है ...
एसजेड।

34

हैकिंग का एक सा है, इसलिए सिर्फ मनोरंजन के लिए साझा करना।

#define with(T, ...)\
    ([&]{ T ${}; __VA_ARGS__; return $; }())

और इसका उपयोग करें:

MyFunction(with(Params,
    $.Name = "Foo Bar",
    $.Age  = 18
));

जिसका विस्तार है:

MyFunction(([&] {
 Params ${};
 $.Name = "Foo Bar", $.Age = 18;
 return $;
}()));

नीट, $प्रकार नाम के एक चर के साथ एक लंबो बनाता है T, और आप इसे वापस करने से पहले अपने सदस्यों को सीधे असाइन करते हैं। निफ्टी। मुझे आश्चर्य है कि अगर इसके साथ कोई प्रदर्शन चिंताएं हैं।
टैंकरस्मैश

1
एक अनुकूलित बिल्ड में आपको लैम्ब्डा का कोई निशान नहीं दिखता है और न ही इसका आह्वान। यह सब इनबिल्ड है।
कीबस

1
मुझे यह जवाब बिल्कुल पसंद है।
सिपाही रीड

6
ओह। $ भी एक वैध नाम नहीं था।
क्रिस वाट्स

यह विरासत सी संकलकों द्वारा समर्थित था और समर्थन पिछड़े संगतता के लिए रुका हुआ था।
कीबस

22

वर्तमान में तैयार किया गया इनिशियलाइज़र C ++ 20 बॉडी वर्क में शामिल हैं: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf ताकि हम अंत में उन्हें देख सकें!


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

@Ruslan: मुझे आश्चर्य है कि C ++ ने उन्हें इतना प्रतिबंधित क्यों किया? मैं समझता हूं कि इस बात को लेकर भ्रम हो सकता है कि जिस क्रम में वस्तुओं के मूल्यों का मूल्यांकन किया जाता है और / या जो संरचना में लिखा जाता है, वह उस क्रम से मेल खाता है, जिसमें आइटम वैश्वीकरण सूची में निर्दिष्ट होते हैं, या वह क्रम जिसमें सदस्य संरचना में दिखाई देते हैं, लेकिन इसका समाधान केवल यह कहना होगा कि आरंभिक अभिव्यक्तियों को मनमाना अनुक्रम में निष्पादित किया जाता है, और ऑब्जेक्ट का जीवनकाल तब तक शुरू नहीं होता है जब तक कि आरंभीकरण पूरा नहीं हो जाता है ( &ऑपरेटर उस पते को वापस कर देगा जो ऑब्जेक्ट अपने जीवनकाल के दौरान होगा )।
सुपरकैट

5

दो मुख्य C99 की विशेषताएं हैं कि C ++ 11 Lacks में "नामित शुरुआती और C ++" का उल्लेख है।

मुझे लगता है कि संभावित अनुकूलन के साथ संबंधित 'नामित इनिशियलाइज़र'। यहाँ मैं एक उदाहरण के रूप में "gcc / g ++" 5.1 का उपयोग करता हूँ।

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>    
struct point {
    int x;
    int y;
};
const struct point a_point = {.x = 0, .y = 0};
int foo() {
    if(a_point.x == 0){
        printf("x == 0");
        return 0;
    }else{
        printf("x == 1");
        return 1;
    }
}
int main(int argc, char *argv[])
{
    return foo();
}

हम संकलन समय पर जानते थे, a_point.xशून्य है, इसलिए हम उम्मीद कर सकते हैं कि fooएक एकल में अनुकूलित है printf

$ gcc -O3 a.c
$ gdb a.out
(gdb) disassemble foo
Dump of assembler code for function foo:
   0x00000000004004f0 <+0>: sub    $0x8,%rsp
   0x00000000004004f4 <+4>: mov    $0x4005bc,%edi
   0x00000000004004f9 <+9>: xor    %eax,%eax
   0x00000000004004fb <+11>:    callq  0x4003a0 <printf@plt>
   0x0000000000400500 <+16>:    xor    %eax,%eax
   0x0000000000400502 <+18>:    add    $0x8,%rsp
   0x0000000000400506 <+22>:    retq   
End of assembler dump.
(gdb) x /s 0x4005bc
0x4005bc:   "x == 0"

foox == 0केवल प्रिंट करने के लिए अनुकूलित है ।

C ++ संस्करण के लिए,

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
struct point {
    point(int _x,int _y):x(_x),y(_y){}
    int x;
    int y;
};
const struct point a_point(0,0);
int foo() {
    if(a_point.x == 0){
        printf("x == 0");
        return 0;
    }else{
        printf("x == 1");
        return 1;
    }
}
int main(int argc, char *argv[])
{
    return foo();
}

और यह अनुकूलित असेंबल कोड का आउटपुट है।

g++ -O3 a.cc
$ gdb a.out
(gdb) disassemble foo
Dump of assembler code for function _Z3foov:
0x00000000004005c0 <+0>:    push   %rbx
0x00000000004005c1 <+1>:    mov    0x200489(%rip),%ebx        # 0x600a50 <_ZL7a_point>
0x00000000004005c7 <+7>:    test   %ebx,%ebx
0x00000000004005c9 <+9>:    je     0x4005e0 <_Z3foov+32>
0x00000000004005cb <+11>:   mov    $0x1,%ebx
0x00000000004005d0 <+16>:   mov    $0x4006a3,%edi
0x00000000004005d5 <+21>:   xor    %eax,%eax
0x00000000004005d7 <+23>:   callq  0x400460 <printf@plt>
0x00000000004005dc <+28>:   mov    %ebx,%eax
0x00000000004005de <+30>:   pop    %rbx
0x00000000004005df <+31>:   retq   
0x00000000004005e0 <+32>:   mov    $0x40069c,%edi
0x00000000004005e5 <+37>:   xor    %eax,%eax
0x00000000004005e7 <+39>:   callq  0x400460 <printf@plt>
0x00000000004005ec <+44>:   mov    %ebx,%eax
0x00000000004005ee <+46>:   pop    %rbx
0x00000000004005ef <+47>:   retq   

हम देख सकते हैं कि a_pointवास्तव में एक संकलन समय स्थिर मूल्य नहीं है।


8
अब कृपया प्रयास करें constexpr point(int _x,int _y):x(_x),y(_y){}। clang ++ का ऑप्टिमाइज़र आपके कोड की तुलना को भी समाप्त करता है। तो, यह सिर्फ एक क्यूओआई मुद्दा है।
DYP

मुझे पूरी a_point ऑब्जेक्ट से यह उम्मीद करनी होगी कि अगर यह आंतरिक लिंकेज है तो इसे दूर किया जाएगा। यानी इसे अनाम नामस्थान में रखें और देखें कि क्या होता है। goo.gl/wNL0HC
Arvid

@dyp: यहां तक ​​कि एक कंस्ट्रक्टर को परिभाषित करना केवल तभी संभव है जब प्रकार आपके नियंत्रण में हो। आप ऐसा नहीं कर सकते, उदाहरण के लिए, struct addrinfoया struct sockaddr_in, इसलिए आप घोषणाओं से अलग असाइनमेंट के साथ रह गए हैं।
मुसीफिल

2
@musiphil कम से कम C ++ 14 में, उन C- शैली की संरचना को एक कॉन्स्ट्रेक्स फ़ंक्शन में ठीक से स्थानीय चर के रूप में असाइनमेंट का उपयोग करके सेट किया जा सकता है, और फिर उस फ़ंक्शन से वापस आ सकते हैं। इसके अतिरिक्त, मेरी बात सी ++ में कंस्ट्रक्टर के वैकल्पिक कार्यान्वयन को दिखाने के लिए नहीं थी जो अनुकूलन की अनुमति देता है, लेकिन यह प्रदर्शित करता है कि कंपाइलर के लिए यह अनुकूलन प्रदर्शन करना संभव है यदि प्रारंभ का रूप अलग है। यदि संकलक "काफी अच्छा" है (यानी अनुकूलन के इस रूप का समर्थन करता है), तो यह अप्रासंगिक होना चाहिए कि क्या आप एक ctor या नामित शुरुआती, या कुछ और का उपयोग करते हैं।
DYP
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.