वैरिएबल पर कॉन्स्टैप्ट बनाम कॉन्स्टैक्स


303

क्या निम्नलिखित परिभाषाओं के बीच अंतर है?

const     double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;

यदि नहीं, तो C ++ 11 में कौन सी शैली पसंद की जाती है?



दोनों संकलन-समय स्थिर हैं। लेकिन आप पहले एक const_cast कर सकते हैं और इसे लिख सकते हैं। लेकिन यह किसी भी संकलक द्वारा अनुकूलित किया जाएगा क्योंकि यह "रीड्स" को प्रभावित नहीं करता है क्योंकि वे संकलन-समय पर होते हैं।
बोनिता मोंटेरो

जवाबों:


347

मेरा मानना ​​है कि एक अंतर है। आइए उनका नाम बदलें ताकि हम उनके बारे में अधिक आसानी से बात कर सकें:

const     double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;

दोनों PI1और PI2जिसका अर्थ है कि आप उन्हें संशोधित नहीं कर सकते लगातार कर रहे हैं। हालांकि केवल PI2 एक संकलन-समय स्थिर है। इसका संकलन समय पर किया जाएगाPI1संकलन समय या रन समय पर आरंभ किया जा सकता है। इसके अलावा, केवल PI2 एक संदर्भ में उपयोग किया जा सकता है जिसके लिए एक संकलन-समय की आवश्यकता होती है। उदाहरण के लिए:

constexpr double PI3 = PI1;  // error

परंतु:

constexpr double PI3 = PI2;  // ok

तथा:

static_assert(PI1 == 3.141592653589793, "");  // error

परंतु:

static_assert(PI2 == 3.141592653589793, "");  // ok

किसके अनुसार आपको उपयोग करना चाहिए? जो भी आपकी आवश्यकताओं को पूरा करता है उसका उपयोग करें। क्या आप यह सुनिश्चित करना चाहते हैं कि आपके पास एक संकलन समय स्थिर है जो संदर्भों में उपयोग किया जा सकता है जहां एक संकलन-समय स्थिरांक की आवश्यकता है? क्या आप इसे रन टाइम में किए गए कंपीटिशन के साथ शुरू करना चाहते हैं? आदि।


60
क्या आपको यकीन है? क्योंकि const int N = 10; char a[N];काम करता है, और सरणी सीमाएँ संकलन-समय स्थिरांक होनी चाहिए।
fredoverflow

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

34
@FredOverflow: नॉन-कास्ट ऐरे इंडेक्स ने लगभग एक दशक तक "काम किया है" (उदाहरण के लिए इसके लिए g ++ एक्सटेंशन है), लेकिन इसका मतलब यह नहीं है कि यह कड़ाई से कानूनी C ++ है (हालांकि कुछ और हालिया C या C ++ मानक ने इसे कानूनी बना दिया है , I जो भूल गया)। Compiletime स्थिरांक, टेम्पलेट मानकों और उपयोग में मतभेद के रूप में के रूप में enumप्रारंभकर्ता केवल दोनों के बीच उल्लेखनीय मतभेद हैं constऔर constexpr(और न के लिए काम करता है doubleवैसे भी)।
डेमन

17
५.१ ९ में से ४.१ ९ के लगातार भाव [expr.const] भी एक (गैर-आदर्शवादी) नोट है जो प्रसिद्ध रूप से रेखांकित करता है कि एक कार्यान्वयन को रनटाइम की तुलना में संकलन-समय पर अलग-अलग तरीके से (जैसे सटीकता के संबंध में) अंकगणित करने की अनुमति है। इसलिए 1 / PI1और 1 / PI2अलग परिणाम दे सकते हैं। मुझे नहीं लगता कि इस उत्तर में सलाह के रूप में यह तकनीकी काफी महत्वपूर्ण है।
ल्यूक डैंटन

4
लेकिन यह constexpr double PI3 = PI1;मेरे लिए सही तरीके से काम करता है। (MSVS2013 CTP)। मैं क्या गलत कर रहा हूं?
NuPagadi

77

यहां कोई अंतर नहीं है, लेकिन यह तब मायने रखता है जब आपके पास एक प्रकार है जिसमें एक कंस्ट्रक्टर है।

struct S {
    constexpr S(int);
};

const S s0(0);
constexpr S s1(1);

s0एक स्थिर है, लेकिन यह संकलन-समय पर आरंभ करने का वादा नहीं करता है। s1चिह्नित किया गया है constexpr, इसलिए यह एक स्थिर और है, क्योंकि Sनिर्माणकर्ता भी चिह्नित है constexpr, इसे संकलन-समय पर आरंभ किया जाएगा।

ज्यादातर यह तब मायने रखता है जब रनटाइम पर इनिशियलाइज़ेशन समय लेने वाला होता है और आप उस काम को कंपाइलर पर छोड़ना चाहते हैं, जहाँ यह समय लेने वाला भी होता है, लेकिन संकलित प्रोग्राम के निष्पादन समय को धीमा नहीं करता है।


3
मैं सहमत हूं: मैं जिस निष्कर्ष पर पहुंचा था, वह constexprयह था कि निदान के लिए वस्तु का संकलन-समय गणना असंभव होना चाहिए। यह कम स्पष्ट है कि क्या एक निरंतर पैरामीटर की अपेक्षा करने वाले फ़ंक्शन को संकलन-समय पर निष्पादित किया जा सकता है constया नहीं , पैरामीटर को घोषित किया constexprजाना constexpr int foo(S)चाहिए : यानी, यदि मैं कॉल करता हूं, तो संकलन-समय पर निष्पादित किया जाएगा foo(s0)?
मैथ्यू एम।

4
@ मैथ्यूएमएम: मुझे संदेह है कि क्या foo(s0)संकलन-समय पर निष्पादित किया जाएगा, लेकिन आप कभी नहीं जानते हैं: एक कंपाइलर को इस तरह के अनुकूलन करने की अनुमति है। निश्चित रूप से, न तो 4.7.2 और न ही constexpr a = foo(s0);
क्लैंग

50

constexpr एक मान इंगित करता है जो संकलन के दौरान स्थिर और ज्ञात है।
const एक मूल्य को इंगित करता है जो केवल स्थिर है; संकलन के दौरान यह जानना अनिवार्य नहीं है।

int sz;
constexpr auto arraySize1 = sz;    // error! sz's value unknown at compilation
std::array<int, sz> data1;         // error! same problem

constexpr auto arraySize2 = 10;    // fine, 10 is a compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2 is constexpr

ध्यान दें कि कॉन्स्टैक्स के रूप में कॉन्स्टेंट समान गारंटी नहीं देता है, क्योंकि कॉम्प्लेक्स ऑब्जेक्ट्स को संकलन के दौरान ज्ञात मूल्यों के साथ आरंभीकृत नहीं किया जाना चाहिए।

int sz;
const auto arraySize = sz;       // fine, arraySize is const copy of sz
std::array<int, arraySize> data; // error! arraySize's value unknown at compilation

सभी कॉन्स्टैक्स प्रॉप्स कॉन्सट हैं, लेकिन सभी कॉन्सटेप ऑब्जेक्ट्स कॉन्सटेक्स्ट नहीं हैं।

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


2
मुझे आपका स्पष्टीकरण बहुत पसंद आया..यदि आप कृपया अधिक टिप्पणी करें कि हम वास्तविक जीवन परिदृश्यों में संकलन समय स्थिरांक का उपयोग करने की आवश्यकता के मामले कहां हैं।
मयूख सरकार

1
@MayukhSarkar सिर्फ़ Google C ++ ही क्यों कॉन्स्टैक्स , उदाहरण के लिए stackoverflow.com/questions/4748083/…
अंडरस्कोर_ड

18

एक कॉन्सटेक्स प्रतीकात्मक स्थिरांक को एक मूल्य दिया जाना चाहिए जो संकलन समय पर जाना जाता है। उदाहरण के लिए:

constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    constexpr int c2 = n+7;   // Error: we don’t know the value of c2
    // ...
}

उन मामलों को संभालने के लिए जहां "वैरिएबल" का मूल्य एक ऐसे मूल्य के साथ आरम्भ किया जाता है, जो संकलन समय पर ज्ञात नहीं है, लेकिन आरंभीकरण के बाद कभी नहीं बदलता है, C ++ निरंतर (एक कॉन्स्ट ) का दूसरा रूप प्रदान करता है । उदाहरण के लिए:

constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    const int c2 = n+7; // OK, but don’t try to change the value of c2
    // ...
    c2 = 7; // error: c2 is a const
}

ऐसे " कांस्टेबल चर" दो कारणों से बहुत सामान्य हैं:

  1. C ++ 98 में कॉन्स्टैक्स नहीं था, इसलिए लोग कॉन्स्ट का इस्तेमाल करते थे ।
  2. सूची आइटम "वेरिएबल्स" जो निरंतर अभिव्यक्तियाँ नहीं हैं (उनका मूल्य संकलन समय पर ज्ञात नहीं है) लेकिन मूल्य निर्धारण व्यापक रूप से उपयोगी होने के बाद अपने आप में मूल्य नहीं बदलते हैं।

संदर्भ: "प्रोग्रामिंग: सिद्धांत और अभ्यास सी ++ का उपयोग करके" स्ट्रॉस्ट्रुप द्वारा


25
: "सिद्धांत और व्यवहार सी का प्रयोग ++ प्रोग्रामिंग" Stroustrup द्वारा शायद आप का उल्लेख किया जाना चाहिए था कि अपने जवाब में पाठ से शब्दशः लिया जाता है
AKY
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.