`Constexpr` और` const` के बीच अंतर


593

बीच क्या अंतर है constexprऔर const?

  • मैं उनमें से केवल एक का उपयोग कब कर सकता हूं?
  • मैं दोनों का उपयोग कब कर सकता हूं और मुझे कैसे चुनना चाहिए?

71
constexprएक संकलन-समय स्थिर बनाता है; constबस इसका मतलब है कि मूल्य नहीं बदला जा सकता है।
0x499602D2


यह लेख हो सकता है boost/hanaपुस्तकालय कुछ enlight कर सकते हैं constexprमुद्दों जहां उपयोग कर सकते हैं constexprऔर जहां आप नहीं कर सकते: boost.org/doc/libs/1_69_0/libs/hana/doc/html/...
एंड्री

@ 0x499602D2 "का सीधा मतलब है कि मूल्य को बदला नहीं जा सकता है " एक शाब्दिक के साथ आरंभ किए गए स्केलर के लिए, एक मूल्य जो परिवर्तन नहीं किया जा सकता है वह भी एक संकलन समय स्थिर है।
जिज्ञासु

@curiousguy हाँ मेरी टिप्पणी बहुत ही उपयोगी थी। निश्चित रूप से मैं constexprवापस तो नया था :) :)
0x499602D2

जवाबों:


587

मूल अर्थ और वाक्य रचना

दोनों खोजशब्दों का उपयोग वस्तुओं की घोषणा के साथ-साथ कार्यों में किया जा सकता है। वस्तुओं पर लागू होने पर मूल अंतर यह है:

  • constकिसी वस्तु को स्थिर घोषित करता है । इसका मतलब यह है कि एक बार शुरू होने के बाद, उस वस्तु का मूल्य नहीं बदलेगा, और संकलक अनुकूलन के लिए इस तथ्य का उपयोग कर सकता है। यह प्रोग्रामर को उस कोड को लिखने से रोकने में भी मदद करता है जो उन वस्तुओं को संशोधित करता है, जिन्हें आरंभीकरण के बाद संशोधित नहीं किया जाना था।

  • constexprमानक ऑब्जेक्ट को निरंतर अभिव्यक्तियों के रूप में उपयोग करने के लिए एक वस्तु को फिट घोषित करता है । लेकिन ध्यान दें कि constexprऐसा करने का एकमात्र तरीका नहीं है।

जब बुनियादी अंतर के कार्यों पर लागू किया जाता है:

  • constकेवल गैर-स्थैतिक सदस्य कार्यों के लिए उपयोग किया जा सकता है, सामान्य रूप से कार्य नहीं करता है। यह गारंटी देता है कि सदस्य फ़ंक्शन किसी भी गैर-स्थैतिक डेटा सदस्यों को संशोधित नहीं करता है।

  • constexprदोनों सदस्य और गैर-सदस्य कार्यों, साथ ही साथ निर्माणकर्ताओं के साथ उपयोग किया जा सकता है। यह निरंतर अभिव्यक्तियों में उपयोग के लिए फ़ंक्शन को फिट घोषित करता है । कंपाइलर केवल इसे स्वीकार करेगा यदि फ़ंक्शन कुछ मानदंडों (7.1.5 / 3,4) को पूरा करता है, सबसे महत्वपूर्ण (accept) :

    • फ़ंक्शन बॉडी नॉन-वर्चुअल और बेहद सिंपल होना चाहिए: टाइपिडिफ और स्टैटिक ऐसर्ट के अलावा, केवल एक returnस्टेटमेंट की अनुमति है। एक निर्माणकर्ता के मामले में, केवल एक आरंभीकरण सूची, टंकण और स्थैतिक अभिकर्मक की अनुमति है। ( = defaultऔर = deleteअनुमति दी जाती है, भी, हालांकि)
    • C ++ 14 के रूप में नियम अधिक आराम से हैं, तब से एक कॉन्स्ट्रेक्स फ़ंक्शन के अंदर क्या अनुमति है: asmघोषणा, एक gotoबयान, के अलावा एक लेबल के साथ एक बयान caseऔर default, कोशिश-ब्लॉक, गैर-शाब्दिक प्रकार के एक चर की परिभाषा, स्थिर या थ्रेड भंडारण अवधि के एक चर की परिभाषा, एक चर की परिभाषा जिसके लिए कोई आरंभीकरण नहीं किया जाता है।
    • तर्क और वापसी प्रकार शाब्दिक प्रकार होना चाहिए (जैसे, आम तौर पर बोलना, बहुत सरल प्रकार, आमतौर पर स्केलर या समुच्चय)

लगातार अभिव्यक्ति

जैसा कि ऊपर कहा गया है, constexprदोनों वस्तुओं के साथ-साथ कार्यों को निरंतर अभिव्यक्तियों में उपयोग के लिए फिट घोषित करता है। एक स्थिर अभिव्यक्ति केवल स्थिर से अधिक है:

  • इसका उपयोग उन स्थानों पर किया जा सकता है जिनके लिए संकलन-समय मूल्यांकन की आवश्यकता होती है, उदाहरण के लिए, टेम्पलेट पैरामीटर और सरणी-आकार निर्दिष्टकर्ता:

    template<int N>
    class fixed_size_list
    { /*...*/ };
    
    fixed_size_list<X> mylist;  // X must be an integer constant expression
    
    int numbers[X];  // X must be an integer constant expression
  • लेकिन ध्यान दें:

    • किसी चीज की घोषणा करना constexprजरूरी नहीं है कि यह संकलन समय पर मूल्यांकन किया जाएगा। इसका उपयोग ऐसे किया जा सकता है , लेकिन इसका उपयोग अन्य स्थानों पर भी किया जा सकता है, जिनका मूल्यांकन रन-टाइम पर किया जाता है, साथ ही साथ।

    • एक वस्तु को घोषित किए बिना निरंतर अभिव्यक्तियों में उपयोग के लिए फिट हो सकता है constexpr। उदाहरण:

      int main()
      {
        const int N = 3;
        int numbers[N] = {1, 2, 3};  // N is constant expression
      }

    यह संभव है N, क्योंकि एक शाब्दिक के साथ घोषणा के समय में निरंतर और प्रारंभिक होने पर, एक स्थिर अभिव्यक्ति के मानदंड को संतुष्ट करता है, भले ही वह घोषित न हो constexpr

तो मुझे वास्तव में कब उपयोग करना है constexpr?

  • एक वस्तु की तरह Nऊपर निरंतर अभिव्यक्ति के रूप में इस्तेमाल किया जा सकता बिना घोषित किया गया constexpr। यह सभी वस्तुओं के लिए सही है:

    • const
    • अभिन्न या गणना प्रकार और
    • एक अभिव्यक्ति के साथ घोषणा के समय पर शुरू की गई जो खुद एक स्थिर अभिव्यक्ति है

    [यह §5.19 / 2 के कारण है: एक निरंतर अभिव्यक्ति में एक सबएक्सप्रेस शामिल नहीं होना चाहिए जिसमें "जब तक […] एक अभिन्न या अभिज्ञान प्रकार का एक चमक शामिल है [...]" "रिचर्ड स्मिथ के लिए धन्यवाद मेरा धन्यवाद। पहले दावा करते हैं कि यह सभी शाब्दिक प्रकारों के लिए सच था।]

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

    template<int N>
    class list
    { };
    
    constexpr int sqr1(int arg)
    { return arg * arg; }
    
    int sqr2(int arg)
    { return arg * arg; }
    
    int main()
    {
      const int X = 2;
      list<sqr1(X)> mylist1;  // OK: sqr1 is constexpr
      list<sqr2(X)> mylist2;  // wrong: sqr2 is not constexpr
    }

मुझे कब / दोनों का उपयोग करना चाहिए, constऔर constexpr एक साथ?

A. वस्तु घोषणाओं में। यह कभी भी आवश्यक नहीं होता है जब दोनों कीवर्ड एक ही ऑब्जेक्ट को घोषित करने के लिए संदर्भित करते हैं। constexprतात्पर्य है const

constexpr const int N = 5;

के समान है

constexpr int N = 5;

हालाँकि, ध्यान दें कि ऐसी स्थितियाँ हो सकती हैं जब कीवर्ड प्रत्येक घोषणा के विभिन्न हिस्सों को संदर्भित करते हैं:

static constexpr int N = 3;

int main()
{
  constexpr const int *NP = &N;
}

यहाँ, NPएक पता स्थिर-अभिव्यक्ति के रूप में घोषित किया जाता है, अर्थात एक सूचक जो स्वयं एक स्थिर अभिव्यक्ति है। (यह तब संभव है, जब पता ऑपरेटर को स्थिर / वैश्विक स्थिर अभिव्यक्ति पर लागू करने से पता उत्पन्न होता है।) यहां, दोनों की आवश्यकता है constexprऔर const: constexprहमेशा अभिव्यक्ति को संदर्भित किया जा रहा है (यहां NP), जबकि constसंदर्भित करता है int(यह एक सूचक घोषित करता है- टू-स्थिरांक)। हटाने से constअभिव्यक्ति को गैरकानूनी बना दिया जाएगा (क्योंकि (ए) गैर-कॉन्स्टेंट ऑब्जेक्ट के लिए एक पॉइंटर एक स्थिर अभिव्यक्ति नहीं हो सकता है, और (बी) &Nवास्तव में एक पॉइंटर-टू-कंटस्टेंट है)।

B. सदस्य कार्य घोषणाओं में। C ++ 11 में, constexprइसका मतलब है const, जबकि C ++ 14 और C ++ 17 में ऐसा नहीं है। C ++ 11 के तहत घोषित एक सदस्य समारोह

constexpr void f();

के रूप में घोषित करने की आवश्यकता है

constexpr void f() const;

C ++ 14 के तहत एक constसमारोह के रूप में अभी भी प्रयोग करने योग्य होने के लिए ।


3
IMO "संकलन समय पर आवश्यक रूप से मूल्यांकन नहीं किया जाता है" उनके बारे में सोचने से कम मददगार होता है क्योंकि "संकलन समय पर मूल्यांकन किया जाता है"। एक स्थिर अभिव्यक्ति पर बाधाओं का मतलब है कि एक कंपाइलर के लिए इसका मूल्यांकन करना अपेक्षाकृत आसान होगा। यदि उन बाधाओं को संतुष्ट नहीं किया जाता है तो एक कंपाइलर को शिकायत करनी चाहिए। चूंकि कोई साइड इफेक्ट नहीं हैं, आप कभी भी एक अंतर नहीं बता सकते हैं कि एक संकलक ने "मूल्यांकन" किया या नहीं।
aschepler

10
@aschepler ज़रूर। मेरा मुख्य बिंदु यह है कि यदि आप constexprएक गैर-स्थिर अभिव्यक्ति पर एक फ़ंक्शन कहते हैं , उदाहरण के लिए एक साधारण चर, यह पूरी तरह से कानूनी है और फ़ंक्शन किसी अन्य फ़ंक्शन की तरह उपयोग किया जाएगा। यह संकलन समय पर मूल्यांकन नहीं किया जाएगा (क्योंकि यह नहीं हो सकता है)। शायद आपको लगता है कि यह स्पष्ट है - लेकिन अगर मैंने कहा कि एक फ़ंक्शन के रूप में घोषित किया गया है, constexprतो हमेशा संकलन-समय पर मूल्यांकन किया जाएगा, तो इसे गलत तरीके से व्याख्या किया जा सकता है।
जोगोजपन

5
हां, मैं constexprवस्तुओं के बारे में बात कर रहा था , न कि कार्यों के बारे में । मैं constexprवस्तुओं के बारे में सोचने के लिए मानों के संकलन समय संकलन को मजबूर करना पसंद करता हूं , और constexprफ़ंक्शन के रूप में फ़ंक्शन को संकलन समय पर चलाने या उपयुक्त समय के रूप में चलाने की अनुमति देता हूं ।
aschepler

2
एक सुधार: 'कास्ट' केवल एक प्रतिबंध है कि आप एक चर के मूल्य को बदल नहीं सकते; यह कोई वादा नहीं करता है कि मूल्य में बदलाव नहीं होगा (यानी, किसी और के द्वारा)। यह एक लिखने की संपत्ति है, पढ़ने की संपत्ति नहीं है।
जारेड ग्रब

3
यह वाक्य: यह गारंटी देता है कि सदस्य फ़ंक्शन किसी भी गैर-स्थैतिक डेटा सदस्यों को संशोधित नहीं करता है। एक महत्वपूर्ण विवरण याद आती है। के रूप में चिह्नित सदस्यों mutableको भी constसदस्य कार्यों द्वारा संशोधित किया जा सकता है ।
सर्वव्यापी

119

constचर के लिए लागू होता है , और उन्हें आपके कोड में संशोधित होने से रोकता है

constexprसंकलक को बताता है कि इस अभिव्यक्ति के परिणामस्वरूप एक स्थिर समय निरंतर मूल्य होता है , इसलिए इसका उपयोग सरणी लंबाई, constचर को असाइन करने , आदि स्थानों में किया जा सकता है। ओली द्वारा दिए गए लिंक में बहुत सारे उत्कृष्ट उदाहरण हैं।

मूल रूप से वे 2 अलग-अलग अवधारणाएं हैं, और (और) का एक साथ उपयोग किया जाना चाहिए।



64

अवलोकन

  • constगारंटी देता है कि एक कार्यक्रम एक वस्तु का मूल्य नहीं बदलता है । हालांकि, constयह गारंटी नहीं देता है कि ऑब्जेक्ट किस प्रकार के आरंभीकरण से गुजरता है।

    विचार करें:

    const int mx = numeric_limits<int>::max();  // OK: runtime initialization

    फ़ंक्शन max()केवल एक शाब्दिक मान लौटाता है। हालाँकि, क्योंकि इनिशलाइज़र एक फ़ंक्शन कॉल है, mxरनटाइम इनिशियलाइज़ेशन से गुजरता है। इसलिए, आप इसे एक स्थिर अभिव्यक्ति के रूप में उपयोग नहीं कर सकते हैं :

    int arr[mx];  // error: “constant expression required”
  • constexprएक नया C ++ 11 कीवर्ड है जो आपको मैक्रोज़ और हार्डकोडेड शाब्दिक बनाने की आवश्यकता के बारे में बताता है। यह कुछ शर्तों के तहत भी गारंटी देता है, कि वस्तुएं स्थैतिक आरंभ से गुजरती हैं । यह एक अभिव्यक्ति के मूल्यांकन के समय को नियंत्रित करता है। इसकी अभिव्यक्ति के संकलन-समय के मूल्यांकन को लागू करने से , constexprआप वास्तविक निरंतर अभिव्यक्तियों को परिभाषित कर सकते हैं जो समय-महत्वपूर्ण अनुप्रयोगों, सिस्टम प्रोग्रामिंग, टेम्पलेट्स और आम तौर पर बोलने के लिए महत्वपूर्ण हैं, जो किसी भी कोड में संकलन-समय स्थिरांक पर निर्भर करता है।

लगातार अभिव्यक्ति के कार्य

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

एक निरंतर-अभिव्यक्ति फ़ंक्शन का मतलब प्रदर्शन या प्रकार की सुरक्षा के त्याग के बिना मैक्रोज़ और हार्डकोडेड शाब्दिक को बदलना है ।

constexpr int max() { return INT_MAX; }           // OK
constexpr long long_max() { return 2147483647; }  // OK
constexpr bool get_val()
{
    bool res = false;
    return res;
}  // error: body is not just a return statement

constexpr int square(int x)
{ return x * x; }  // OK: compile-time evaluation only if x is a constant expression
const int res = square(5);  // OK: compile-time evaluation of square(5)
int y = getval();
int n = square(y);          // OK: runtime evaluation of square(y)

लगातार अभिव्यक्ति वस्तुओं

एक स्थिर-अभिव्यक्ति ऑब्जेक्ट घोषित ऑब्जेक्ट है constexpr। इसे निरंतर अभिव्यक्ति के साथ आरंभ किया जाना चाहिए या निरंतर-अभिव्यक्ति तर्क के साथ एक स्थिर-अभिव्यक्ति निर्माता द्वारा निर्मित एक प्रतिद्वंद्विता।

एक स्थिर-अभिव्यक्ति ऑब्जेक्ट ऐसा व्यवहार करता है जैसे कि यह घोषित किया गया था const, सिवाय इसके कि उपयोग से पहले इसे आरंभीकरण की आवश्यकता होती है और इसके इनिशलाइज़र को एक स्थिर अभिव्यक्ति होना चाहिए। नतीजतन, एक निरंतर-अभिव्यक्ति ऑब्जेक्ट को हमेशा किसी अन्य स्थिर अभिव्यक्ति के भाग के रूप में उपयोग किया जा सकता है।

struct S
{
    constexpr int two();      // constant-expression function
private:
    static constexpr int sz;  // constant-expression object
};
constexpr int S::sz = 256;
enum DataPacket
{
    Small = S::two(),  // error: S::two() called before it was defined
    Big = 1024
};
constexpr int S::two() { return sz*2; }
constexpr S s;
int arr[s.two()];  // OK: s.two() called after its definition

लगातार अभिव्यक्ति निर्माता

एक निरंतर अभिव्यक्ति कंस्ट्रक्टर घोषित एक कंस्ट्रक्टर है constexpr। इसकी एक सदस्य आरंभीकरण सूची हो सकती है, लेकिन इसके शरीर को खाली होना चाहिए, इसके अलावा टाइपडेफ्स और स्टैटिक ऐसर्ट्स। इसके तर्कों में शाब्दिक प्रकार होने चाहिए।

एक स्थिर-अभिव्यक्ति कंस्ट्रक्टर कंपाइलर को संकलन-समय पर ऑब्जेक्ट को इनिशियलाइज़ करने की अनुमति देता है, बशर्ते कि कंस्ट्रक्टर के तर्क सभी निरंतर अभिव्यक्ति हों।

struct complex
{
    // constant-expression constructor
    constexpr complex(double r, double i) : re(r), im(i) { }  // OK: empty body
    // constant-expression functions
    constexpr double real() { return re; }
    constexpr double imag() { return im; }
private:
    double re;
    double im;
};
constexpr complex COMP(0.0, 1.0);         // creates a literal complex
double x = 1.0;
constexpr complex cx1(x, 0);              // error: x is not a constant expression
const complex cx2(x, 1);                  // OK: runtime initialization
constexpr double xx = COMP.real();        // OK: compile-time initialization
constexpr double imaglval = COMP.imag();  // OK: compile-time initialization
complex cx3(2, 4.6);                      // OK: runtime initialization

स्कॉट मेयर्स की पुस्तक इफेक्टिव मॉडर्न C ++ की टिप्स constexpr:

  • constexpr ऑब्जेक्ट्स कॉस्ट हैं और संकलन के दौरान ज्ञात मूल्यों के साथ आरंभीकृत होते हैं;
  • constexpr फ़ंक्शन संकलन-समय परिणाम उत्पन्न करते हैं जब उन तर्कों के साथ बुलाया जाता है जिनके मूल्यों को संकलन के दौरान जाना जाता है;
  • constexprवस्तुओं और कार्यों का उपयोग गैर- constexprवस्तुओं और कार्यों की तुलना में व्यापक संदर्भों में किया जा सकता है ;
  • constexpr किसी ऑब्जेक्ट या फ़ंक्शन के इंटरफ़ेस का हिस्सा है।

स्रोत: C ++ में सुरक्षा, प्रदर्शन और इनकैप्सुलेशन में सुधार के लिए कॉन्स्टैक्स का उपयोग करना


विभिन्न स्थितियों को दिखाने वाले महान उदाहरण कोड के लिए धन्यवाद। अन्य व्याख्याओं में से कुछ के रूप में महान, मैं कार्रवाई में कोड देख अधिक उपयोगी और समझ में आता है। इसने वास्तव में मेरी समझ को मजबूत करने में मदद की कि क्या चल रहा है।
RTHarston

35

ब्रेज़न स्ट्रॉस्ट्रुप द्वारा "द सी ++ प्रोग्रामिंग लैंग्वेज 4 के एडिटन" की पुस्तक के अनुसार
कास्ट : मतलब मोटे तौर पर '' मैं इस मूल्य को नहीं बदलने का वादा करता हूं '' (.57.5)। यह मुख्य रूप से इंटरफेस को निर्दिष्ट करने के लिए उपयोग किया जाता है, ताकि डेटा को संशोधित किए जाने के डर के बिना फ़ंक्शन को पास किया जा सके।
कंपाइलर कॉन्स्ट द्वारा किए गए वादे को लागू करता है।
विवश : जिसका अर्थ है मोटे तौर पर '' संकलन समय पर मूल्यांकन किया जाना '' (.410.4)। इसका उपयोग मुख्य रूप से स्थिरांक निर्दिष्ट करने के लिए किया जाता है,
उदाहरण के लिए:

const int dmv = 17; // dmv is a named constant
int var = 17; // var is not a constant
constexpr double max1 = 1.4*square(dmv); // OK if square(17) is a constant expression
constexpr double max2 = 1.4square(var); // error : var is not a constant expression
const double max3 = 1.4square(var); //OK, may be evaluated at run time
double sum(const vector<double>&); // sum will not modify its argument (§2.2.5)
vector<double> v {1.2, 3.4, 4.5}; // v is not a constant
const double s1 = sum(v); // OK: evaluated at run time
constexpr double s2 = sum(v); // error : sum(v) not constant expression

एक समारोह के एक निरंतर अभिव्यक्ति, यह है कि, एक अभिव्यक्ति में है कि संकलक द्वारा मूल्यांकन किया जाएगा में प्रयोग करने योग्य होने के लिए, यह परिभाषित किया जाना चाहिए constexpr
उदाहरण के लिए:

constexpr double square(double x) { return xx; }


बाधा बनने के लिए, एक फ़ंक्शन बल्कि सरल होना चाहिए: केवल एक रिटर्न-स्टेटमेंट एक मान कंप्यूटिंग। एक कॉन्स्ट्रेक्स फ़ंक्शन का उपयोग गैर-स्थिर तर्क के लिए किया जा सकता है, लेकिन जब ऐसा किया जाता है तो परिणाम एक स्थिर अभिव्यक्ति नहीं है। हम संदर्भों में गैर-स्थिर-अभिव्यक्ति तर्कों के साथ एक कॉन्स्ट्रेक्स फ़ंक्शन को कॉल करने की अनुमति देते हैं, जिन्हें निरंतर अभिव्यक्तियों की आवश्यकता नहीं होती है, ताकि हम अनिवार्य रूप से एक ही फ़ंक्शन को दो बार परिभाषित करने के लिए ईवी न करें: निरंतर अभिव्यक्तियों के लिए एक बार और एक बार चर के लिए।
कुछ स्थानों पर, भाषा नियमों (जैसे, सरणी सीमाएँ (.22.2.5, §7.3), केस लेबल (§2.2.4, §9.4.2), कुछ टेम्पलेट तर्क (§25.2) और द्वारा निरंतर अभिव्यक्ति की आवश्यकता होती है। कॉन्स्टेंटों ने कॉन्स्टैक्स का उपयोग करके घोषित किया)। अन्य मामलों में, प्रदर्शन के लिए संकलन-समय मूल्यांकन महत्वपूर्ण है। स्वतंत्र रूप से प्रदर्शन के मुद्दों पर, अपरिवर्तनीयता (एक अपरिवर्तनीय राज्य के साथ एक वस्तु) की धारणा एक महत्वपूर्ण डिजाइन चिंता है (concern10.4)।


अभी भी प्रदर्शन के मुद्दे हैं। लगता है कि रनवे पर मूल्यांकन किया गया तो कॉन्स्टैक्सप्र फ़ंक्शन फ़ंक्शन के नॉन-कॉस्टेक्सप्र संस्करण की तुलना में धीमा हो सकता है। अगर हमारे पास एक निरंतर मूल्य है, तो क्या हमें "कॉन्स्ट" या "कॉन्स्ट्रेप" पसंद करना चाहिए? (अधिक एक शैली प्रश्न उत्पन्न विधानसभा एक ही दिखता है)
कॉफिडेवेलर

31

दोनों constऔर constexprचर और कार्यों के लिए लागू किया जा सकता है। भले ही वे एक-दूसरे के समान हैं, वास्तव में वे बहुत अलग अवधारणाएं हैं।

दोनों constऔर constexprमतलब है कि उनके मूल्यों को उनके प्रारंभ के बाद नहीं बदला जा सकता है। उदाहरण के लिए:

const int x1=10;
constexpr int x2=10;

x1=20; // ERROR. Variable 'x1' can't be changed.
x2=20; // ERROR. Variable 'x2' can't be changed.

के बीच मुख्य अंतर है constऔर constexprवह समय है जब उनके आरंभीकरण मूल्यों को जाना जाता है (मूल्यांकन)। जबकि constचर के मानों का संकलन समय और क्रम दोनों पर किया जा सकता है, constexprहमेशा संकलन समय पर मूल्यांकन किया जाता है। उदाहरण के लिए:

int temp=rand(); // temp is generated by the the random generator at runtime.

const int x1=10; // OK - known at compile time.
const int x2=temp; // OK - known only at runtime.
constexpr int x3=10; // OK - known at compile time.
constexpr int x4=temp; // ERROR. Compiler can't figure out the value of 'temp' variable at compile time so `constexpr` can't be applied here.

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

int temp=rand(); // temp is generated by the the random generator at runtime.

int array1[10]; // OK.
int array2[temp]; // ERROR.

तो इसका मतलब है कि:

const int size1=10; // OK - value known at compile time.
const int size2=temp; // OK - value known only at runtime.
constexpr int size3=10; // OK - value known at compile time.


int array3[size1]; // OK - size is known at compile time.
int array4[size2]; // ERROR - size is known only at runtime time.
int array5[size3]; // OK - size is known at compile time.

तो constचर दोनों संकलन समय स्थिरांक को परिभाषित कर सकते हैं जैसे size1कि सरणी आकार और रनटाइम स्थिरांक को निर्दिष्ट करने के लिए उपयोग किया जा सकता है जैसे size2कि रनटाइम में ही जाना जाता है और सरणी आकार को परिभाषित करने के लिए उपयोग नहीं किया जा सकता है। दूसरी ओर constexprहमेशा संकलन समय स्थिरांक को परिभाषित करें जो सरणी आकार निर्दिष्ट कर सकते हैं।

दोनों constऔर constexprभी कार्यों के लिए लागू किया जा सकता है। एक constफ़ंक्शन एक सदस्य फ़ंक्शन (विधि, ऑपरेटर) होना चाहिए जहां constकीवर्ड के अनुप्रयोग का अर्थ है कि विधि उनके सदस्य (गैर-स्थिर) फ़ील्ड के मूल्यों को नहीं बदल सकती है। उदाहरण के लिए।

class test
{
   int x;

   void function1()
   {
      x=100; // OK.
   }

   void function2() const
   {
      x=100; // ERROR. The const methods can't change the values of object fields.
   }
};

A constexprएक अलग अवधारणा है। यह एक फ़ंक्शन (सदस्य या गैर-सदस्य) को उस फ़ंक्शन के रूप में चिह्नित करता है जिसका संकलन समय पर मूल्यांकन किया जा सकता है यदि संकलन समय स्थिरांक को उनके तर्क के रूप में पारित किया जाता है । उदाहरण के लिए आप इसे लिख सकते हैं।

constexpr int func_constexpr(int X, int Y)
{
    return(X*Y);
}

int func(int X, int Y)
{
    return(X*Y);
}

int array1[func_constexpr(10,20)]; // OK - func_constexpr() can be evaluated at compile time.
int array2[func(10,20)]; // ERROR - func() is not a constexpr function.

int array3[func_constexpr(10,rand())]; // ERROR - even though func_constexpr() is the 'constexpr' function, the expression 'constexpr(10,rand())' can't be evaluated at compile time.

वैसे constexprफ़ंक्शन नियमित C ++ फ़ंक्शन हैं जिन्हें गैर-स्थिर तर्क पास होने पर भी कहा जा सकता है। लेकिन उस स्थिति में आपको गैर-कॉन्स्ट्रेक्स मूल्य मिल रहे हैं।

int value1=func_constexpr(10,rand()); // OK. value1 is non-constexpr value that is evaluated in runtime.
constexpr int value2=func_constexpr(10,rand()); // ERROR. value2 is constexpr and the expression func_constexpr(10,rand()) can't be evaluated at compile time.

constexprभी सदस्य कार्य (विधि), ऑपरेटरों और यहां तक कि निर्माताओं के लिए लागू किया जा सकता है। उदाहरण के लिए।

class test2
{
    static constexpr int function(int value)
    {
        return(value+1);
    }

    void f()
    {
        int x[function(10)];


    }
};

एक और 'पागल' नमूना।

class test3
{
    public:

    int value;

    // constexpr const method - can't chanage the values of object fields and can be evaluated at compile time.
    constexpr int getvalue() const
    {
        return(value);
    }

    constexpr test3(int Value)
        : value(Value)
    {
    }
};


constexpr test3 x(100); // OK. Constructor is constexpr.

int array[x.getvalue()]; // OK. x.getvalue() is constexpr and can be evaluated at compile time.

इसके अलावा, सी में constexpr intमौजूद है, लेकिन यह वर्तनी हैconst int
उत्सुक

8

जैसा कि @ 0x499602d2 पहले ही इंगित कर चुका है, constकेवल यह सुनिश्चित करता है कि आरंभीकरण के बाद एक मान को परिवर्तित नहीं किया जा सकता है, जहां constexpr(C ++ 11 में पेश किया गया है) गारंटी देता है कि चर संकलन समय स्थिर है।
निम्नलिखित उदाहरण पर विचार करें (LearnCpp.com से):

cout << "Enter your age: ";
int age;
cin >> age;

const int myAge{age};        // works
constexpr int someAge{age};  // error: age can only be resolved at runtime

5

A const int varगतिशील रूप से रनटाइम पर एक मान पर सेट किया जा सकता है और एक बार उस मान पर सेट हो जाने के बाद, इसे अब बदला नहीं जा सकता है।

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

यहाँ एक ठोस उदाहरण दिया गया है:

int main(int argc, char*argv[]) {
    const int p = argc; 
    // p = 69; // cannot change p because it is a const
    // constexpr int q = argc; // cannot be, bcoz argc cannot be computed at compile time 
    constexpr int r = 2^3; // this works!
    // r = 42; // same as const too, it cannot be changed
}

ऊपर का स्निपेट ठीक संकलन करता है और मैंने उन लोगों पर टिप्पणी की है जो इसे त्रुटि का कारण बनाते हैं।

यहां मुख्य विचार, ध्यान देने के लिए के विचार कर रहे हैं compile timeऔर run time। नए नवाचारों को C ++ में पेश किया गया है ** know **ताकि रनटाइम पर प्रदर्शन में सुधार के लिए संकलन समय पर यथासंभव कुछ चीजों का इरादा किया जा सके ।


3

मुझे नहीं लगता कि कोई भी उत्तर वास्तव में यह स्पष्ट करता है कि इसके क्या दुष्प्रभाव हैं, या वास्तव में, यह क्या है।

constexprऔर constनाम-स्थान / फ़ाइल-स्कोप समान है जब एक शाब्दिक या अभिव्यक्ति के साथ आरंभ किया जाता है; लेकिन एक फ़ंक्शन के साथ, constकिसी भी फ़ंक्शन द्वारा आरंभीकृत किया जा सकता है, लेकिन constexprएक गैर-कॉन्स्ट्रेक्प (एक फ़ंक्शन जिसे कॉन्स्टैक्स या एक गैर कॉन्स्ट्रेक्स अभिव्यक्ति के साथ चिह्नित नहीं किया गया है) द्वारा संकलित किया जाता है। दोनों constexprऔर constचर के लिए परोक्ष आंतरिक संबंध हैं (अच्छी तरह से वास्तव में, वे अगर -O1 और मजबूत संकलन जोड़ने चरण के लिए प्राप्त करने के लिए जीवित नहीं है, और staticके लिए एक आंतरिक (स्थानीय) लिंकर प्रतीक फेंकना संकलक मजबूर नहीं करता है constया constexprजब पर -O1 या मजबूत, केवल समय यह ऐसा करता है, तो आप चर का पता लेते हैं। constऔर constexprजब तक साथ व्यक्त एक आंतरिक प्रतीक हो जाएगा externयानीextern constexpr/const int i = 3;उपयोग करने की आवश्यकता है)। एक समारोह को constexprबनाता समारोह स्थायी रूप से जोड़ने के स्तर तक पहुंच कभी नहीं (की परवाह किए बिना externया inlineपरिभाषा या -O0 या -Ofast में), जबकि constकभी नहीं करता है, और staticऔर inlineकेवल -O1 और इसके बाद के संस्करण पर इस प्रभाव है। जब किसी फ़ंक्शन द्वारा const/ constexprवैरिएबल को इनिशियलाइज़ किया जाता है constexpr, तो लोड को हमेशा किसी ऑप्टिमाइज़ेशन फ़्लैग के साथ ऑप्टिमाइज़ किया जाता है, लेकिन यह कभी भी ऑप्टिमाइज़ नहीं किया जाता है यदि फ़ंक्शन केवल staticया है inline, या यदि वैरिएबल ए const/ नहीं है constexpr

मानक संकलन (-ओ 0)

#include<iostream>
constexpr int multiply (int x, int y)
{

  return x * y;
}

extern const int val = multiply(10,10);
int main () {
  std::cout << val;
} 

के लिए संकलित करता है

val:
        .long   100  //extra external definition supplied due to extern

main:
        push    rbp
        mov     rbp, rsp
        mov     esi, 100 //substituted in as an immediate
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     eax, 0
        pop     rbp
        ret

__static_initialization_and_destruction_0(int, int):
        . 
        . 
        . 

तथापि

#include<iostream>
const int multiply (int x, int y)
{

  return x * y;
}

const int val = multiply(10,10); //constexpr is an error
int main () {
  std::cout << val;
}

को संकलित करता है

multiply(int, int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], edi
        mov     DWORD PTR [rbp-8], esi
        mov     eax, DWORD PTR [rbp-4]
        imul    eax, DWORD PTR [rbp-8]
        pop     rbp
        ret

main:
        push    rbp
        mov     rbp, rsp
        mov     eax, DWORD PTR val[rip]
        mov     esi, eax
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     eax, 0
        pop     rbp
        ret

__static_initialization_and_destruction_0(int, int):
        . 
        . 
        . 
        mov     esi, 10
        mov     edi, 10
        call    multiply(int, int)
        mov     DWORD PTR val[rip], eax

यह स्पष्ट रूप से पता चलता है कि constexprकी initialisation का कारण बनता है const/constexprफ़ाइल-गुंजाइश चर संकलन समय पर पाए जाते हैं और कोई वैश्विक चिह्न देने में है, जबकि का उपयोग नहीं यह पहले होने की initialisation का कारण बनता है mainरनटाइम पर।

संकलन का उपयोग कर -OIFT

यहां तक ​​कि -Oxt लोड का अनुकूलन नहीं करता है! https://godbolt.org/z/r-mhif , इसलिए आपको चाहिए constexpr


constexprconstexprसमान परिणाम के लिए कार्यों को अन्य कार्यों के अंदर से भी बुलाया जा सकता है । constexprएक समारोह में भी कुछ भी है कि समारोह में संकलन समय पर नहीं किया जा सकता के उपयोग को रोकता है; उदाहरण के लिए, <<ऑपरेटर को कॉल करना std::cout

constexprब्लॉक स्कोप में उसी तरह का व्यवहार किया जाता है, अगर वह एक गैर-कॉन्स्ट्रेक्स फ़ंक्शन द्वारा आरंभीकृत किया जाता है; मूल्य भी तुरंत प्रतिस्थापित किया जाता है।

अंत में, इसका मुख्य उद्देश्य सी के इनलाइन फ़ंक्शन की तरह है, लेकिन यह केवल तभी प्रभावी होता है जब फ़ंक्शन का उपयोग फ़ाइल-स्कोप वैरिएबल को इनिशियलाइज़ करने के लिए किया जाता है (जो फ़ंक्शंस C पर नहीं कर सकते, लेकिन वे C ++ पर कर सकते हैं क्योंकि यह फ़ाइल के डायनामिक इनिशियलाइज़ेशन की अनुमति देता है) स्कोप वैरिएबल), फ़ंक्शन को छोड़कर लिंकर के लिए एक वैश्विक / स्थानीय प्रतीक निर्यात नहीं कर सकता है, यहां तक ​​कि उपयोग करना extern/static, जिसे आप inlineसी पर कर सकते हैं ; ब्लॉक-स्कोप वैरिएबल असाइनमेंट फ़ंक्शंस constexprको C और C ++ के बिना -O1 ऑप्टिमाइज़ेशन का उपयोग करके केवल इनलाइन किया जा सकता है ।


लिंकर पर अच्छा बिंदु। यह सामान्य रूप से कॉन्स्टैक्स का उपयोग करने के लिए अधिक सुरक्षित माना जा सकता है क्योंकि इसके परिणामस्वरूप कम प्रतीक लीक होते हैं?
नील मैकगिल

1
@NeilMcGill वास्तव में नहीं है क्योंकि इनलाइन और स्टेटिक कंपाइलर का उपयोग करते हुए -O1 या अधिक मजबूत को कंपाइल करने के लिए स्थानीय प्रतीक का उत्सर्जन नहीं करने का कारण बनेंगे। कॉन्स्टैक्स केवल वही है जो वैल के लिए लोड का अनुकूलन करता है, लेकिन इसके अलावा यह फ़ंक्शन के पहले स्थिर या इनलाइन लगाने के समान है। मैं कुछ और भी भूल गया था। कॉन्स्टेक्सप्र एकमात्र ऐसा कीवर्ड है जो फंक्शन के लिए प्रतीक का उत्सर्जन नहीं करता है -O0, स्टैटिक और इनलाइन करते हैं
लुईस केल्सी

1

सबसे पहले, दोनों c ++ में क्वालिफायर हैं। एक परिवर्तनीय घोषित कॉन्स्ट को आरंभीकृत किया जाना चाहिए और भविष्य में नहीं बदला जा सकता है। इसलिए आम तौर पर एक कांस्टेबल के रूप में घोषित एक चर का संकलन करने से पहले ही एक मूल्य होगा।

लेकिन, बाधा के लिए यह थोड़ा अलग है।

बाधा के लिए, आप एक अभिव्यक्ति दे सकते हैं जिसका मूल्यांकन कार्यक्रम के संकलन के दौरान किया जा सकता है।

जाहिर है, कॉन्स्ट्रेक्टर के रूप में घोषित किया गया वैरिएबल भविष्य में नक्षत्र की तरह नहीं बदला जा सकता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.