क्या किसी फंक्शन के अंदर स्टैटिक कॉन्सटेक्प चर का मतलब होता है?


193

अगर मेरे पास एक फंक्शन (कहते हैं, एक बड़ी सरणी) के अंदर एक चर है, तो क्या यह दोनों को घोषित करने के लिए समझ में आता है staticऔर constexpr? constexprयह गारंटी देता है कि सरणी को संकलन समय पर बनाया गया है, तो क्या यह staticबेकार होगा?

void f() {
    static constexpr int x [] = {
        // a few thousand elements
    };
    // do something with the array
}

क्या staticवास्तव में उत्पन्न कोड या शब्दार्थ के संदर्भ में वहां कुछ भी किया जा रहा है?

जवाबों:


230

संक्षिप्त उत्तर यह है कि न केवल staticउपयोगी है, यह हमेशा अच्छी तरह से वांछित होने वाला है।

सबसे पहले, ध्यान दें कि staticऔर constexprएक दूसरे से पूरी तरह से स्वतंत्र हैं। staticनिष्पादन के दौरान ऑब्जेक्ट के जीवनकाल को परिभाषित करता है; constexprनिर्दिष्ट करता है कि संकलन के दौरान ऑब्जेक्ट उपलब्ध होना चाहिए। संकलन और निष्पादन समय और स्थान दोनों में असहमति और असहमति है। इसलिए एक बार कार्यक्रम संकलित करने के बाद, constexprअब प्रासंगिक नहीं है।

हर चर घोषित constexprपरोक्ष है constलेकिन constऔर staticलगभग ओर्थोगोनल (के साथ बातचीत के अलावा static constपूर्णांकों।)

C++ऑब्जेक्ट मॉडल (§1.9) कि अन्य सभी से थोड़ा-क्षेत्रों स्मृति के कम से कम एक बाइट पर कब्जा और पते हैं वस्तुओं की आवश्यकता है; इसके अलावा, एक निश्चित समय पर एक कार्यक्रम में सभी ऐसी वस्तुओं का अलग-अलग पता होना चाहिए (पैराग्राफ 6)। स्थानीय गैर-स्थिर स्थिरांक के साथ फ़ंक्शन के हर आह्वान के लिए स्टैक पर एक नया सरणी बनाने के लिए कंपाइलर की बहुत आवश्यकता नहीं होती है, क्योंकि कंपाइलर as-ifसिद्धांत की शरण ले सकता है बशर्ते यह साबित कर सके कि ऐसी कोई अन्य वस्तु नहीं हो सकती है देखे गए।

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

दूसरी ओर, static constसभी पर्यवेक्षकों द्वारा एक स्थानीय वस्तु साझा की जाती है, और इसके बाद भी इसे आरंभीकृत किया जा सकता है, भले ही इसे जिस फ़ंक्शन में परिभाषित किया गया हो उसे कभी नहीं कहा जाता है। तो उपरोक्त में से कोई भी लागू नहीं होता है, और एक संकलक स्वतंत्र है न केवल इसका केवल एक उदाहरण उत्पन्न करने के लिए; यह केवल पढ़ने के लिए भंडारण में इसका एक भी उदाहरण उत्पन्न करने के लिए स्वतंत्र है।

इसलिए आपको static constexprअपने उदाहरण में निश्चित रूप से उपयोग करना चाहिए ।

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


2
@AndrewLazarus, आप constकिसी constऑब्जेक्ट से दूर नहीं जा सकते हैं , केवल एक const X*अंक से a तक X। लेकिन बात यह नहीं है; मुद्दा यह है कि स्वचालित वस्तुओं में स्थैतिक पते नहीं हो सकते। जैसा कि मैंने कहा, constexprसंकलन समाप्त होने के बाद सार्थक होना बंद हो जाता है, इसलिए वहाँ से हटकर कुछ भी नहीं है (और संभवतः कुछ भी नहीं है, क्योंकि ऑब्जेक्ट रनटाइम पर मौजूद होने की गारंटी नहीं है।)
rici

17
मुझे लगता है कि न केवल यह जवाब अविश्वसनीय रूप से भ्रामक है बल्कि आत्म विरोधाभासी भी है। उदाहरण के लिए आप कहते हैं कि आप लगभग हमेशा चाहते हैं staticऔर constexprसमझाते हैं कि वे अलग-अलग काम कर रहे हैं। फिर आप दोनों को संयोजित नहीं करने के लिए एक कारण का उल्लेख करते हैं क्योंकि यह ओडीआर-उपयोग (जो उपयोगी लगता है) की अनदेखी करेगा। ओह और मैं अभी भी नहीं देखता कि स्थैतिक के साथ स्थैतिक का उपयोग क्यों किया जाना चाहिए क्योंकि स्थैतिक रनटाइम सामान के लिए है। आपने कभी नहीं समझाया कि स्थैतिक के साथ स्थैतिक क्यों महत्वपूर्ण है।
void.pointer

2
@ void.pointer: आप अंतिम पैराग्राफ के बारे में सही हैं। मैंने इंट्रो बदल दिया। मुझे लगा कि मैंने इसका महत्व समझाया है static constexpr(यह लगातार सरणी को हर फ़ंक्शन कॉल पर फिर से बनाए जाने से रोकता है), लेकिन मैंने कुछ शब्दों को बदल दिया जो इसे स्पष्ट कर सकते हैं। धन्यवाद।
रिसी

8
संकलन समय स्थिरांक बनाम रनटाइम स्थिरांक का उल्लेख करने के लिए भी उपयोगी हो सकता है। दूसरे शब्दों में, यदि एक constexprस्थिर चर का उपयोग केवल संकलन-समय संदर्भों में किया जाता है और कभी भी रनटाइम की आवश्यकता नहीं होती है, तो इसका staticकोई मतलब नहीं है, क्योंकि जब तक आप रनटाइम को प्राप्त करते हैं, तब तक मूल्य प्रभावी रूप से "इनलाइन" हो जाता है। हालांकि, अगर constexprरनटाइम संदर्भों में उपयोग किया जाता है (दूसरे शब्दों में, इसे अंतर्निहित रूप से constexprपरिवर्तित करने की आवश्यकता होगी const, और रनटाइम कोड के लिए एक भौतिक पते के साथ उपलब्ध है) यह staticओडीआर अनुपालन सुनिश्चित करना चाहेगा , आदि यह मेरी समझ है, कम से कम।
void.pointer

3
मेरी अंतिम टिप्पणी के लिए एक उदाहरण static constexpr int foo = 100;:। ऐसा कोई कारण नहीं है कि कंपाइलर fooशाब्दिक रूप से हर जगह उपयोग को स्थानापन्न न कर सके 100, जब तक कि कोड कुछ ऐसा नहीं कर रहे थे &foo। तो staticपर fooके बाद से इस मामले में कोई उपयोगिता है fooरनटाइम पर मौजूद नहीं है। फिर से कंपाइलर तक।
void.pointer

10

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

निम्नलिखित कोड दर्शाता है कि constexprचर को एक ही बार में (एक ही मान के साथ) कैसे आरंभ किया जाता है, जबकि static constexprनिश्चित रूप से केवल एक बार ही आरंभ किया जाता है।

इसके अलावा कोड के साथ संयोजन के constexprखिलाफ के लाभ की तुलना करता है ।conststatic

#include <iostream>
#include <string>
#include <cassert>
#include <sstream>

const short const_short = 0;
constexpr short constexpr_short = 0;

// print only last 3 address value numbers
const short addr_offset = 3;

// This function will print name, value and address for given parameter
void print_properties(std::string ref_name, const short* param, short offset)
{
    // determine initial size of strings
    std::string title = "value \\ address of ";
    const size_t ref_size = ref_name.size();
    const size_t title_size = title.size();
    assert(title_size > ref_size);

    // create title (resize)
    title.append(ref_name);
    title.append(" is ");
    title.append(title_size - ref_size, ' ');

    // extract last 'offset' values from address
    std::stringstream addr;
    addr << param;
    const std::string addr_str = addr.str();
    const size_t addr_size = addr_str.size();
    assert(addr_size - offset > 0);

    // print title / ref value / address at offset
    std::cout << title << *param << " " << addr_str.substr(addr_size - offset) << std::endl;
}

// here we test initialization of const variable (runtime)
void const_value(const short counter)
{
    static short temp = const_short;
    const short const_var = ++temp;
    print_properties("const", &const_var, addr_offset);

    if (counter)
        const_value(counter - 1);
}

// here we test initialization of static variable (runtime)
void static_value(const short counter)
{
    static short temp = const_short;
    static short static_var = ++temp;
    print_properties("static", &static_var, addr_offset);

    if (counter)
        static_value(counter - 1);
}

// here we test initialization of static const variable (runtime)
void static_const_value(const short counter)
{
    static short temp = const_short;
    static const short static_var = ++temp;
    print_properties("static const", &static_var, addr_offset);

    if (counter)
        static_const_value(counter - 1);
}

// here we test initialization of constexpr variable (compile time)
void constexpr_value(const short counter)
{
    constexpr short constexpr_var = constexpr_short;
    print_properties("constexpr", &constexpr_var, addr_offset);

    if (counter)
        constexpr_value(counter - 1);
}

// here we test initialization of static constexpr variable (compile time)
void static_constexpr_value(const short counter)
{
    static constexpr short static_constexpr_var = constexpr_short;
    print_properties("static constexpr", &static_constexpr_var, addr_offset);

    if (counter)
        static_constexpr_value(counter - 1);
}

// final test call this method from main()
void test_static_const()
{
    constexpr short counter = 2;

    const_value(counter);
    std::cout << std::endl;

    static_value(counter);
    std::cout << std::endl;

    static_const_value(counter);
    std::cout << std::endl;

    constexpr_value(counter);
    std::cout << std::endl;

    static_constexpr_value(counter);
    std::cout << std::endl;
}

संभावित कार्यक्रम आउटपुट:

value \ address of const is               1 564
value \ address of const is               2 3D4
value \ address of const is               3 244

value \ address of static is              1 C58
value \ address of static is              1 C58
value \ address of static is              1 C58

value \ address of static const is        1 C64
value \ address of static const is        1 C64
value \ address of static const is        1 C64

value \ address of constexpr is           0 564
value \ address of constexpr is           0 3D4
value \ address of constexpr is           0 244

value \ address of static constexpr is    0 EA0
value \ address of static constexpr is    0 EA0
value \ address of static constexpr is    0 EA0

जैसा कि आप देख सकते हैं कि स्वयं constexprको कई बार initilized किया गया है (पता समान नहीं है) जबकि staticकीवर्ड यह सुनिश्चित करता है कि आरंभीकरण केवल एक बार किया जाता है।


क्या हम constexpr const short constexpr_shortत्रुटि देने के लिए उपयोग नहीं कर सकते हैं यदि constexpr_short को फिर से प्रारंभ किया गया है
akhileshzmishra

आपके सिंटैक्स का constexpr constकोई मतलब नहीं है क्योंकि constexprपहले से ही है const, constएक बार या कई बार जोड़ना संकलक द्वारा अनदेखा किया जाता है। आप एक त्रुटि पकड़ने की कोशिश कर रहे हैं, लेकिन यह कोई त्रुटि नहीं है, यही कारण है कि अधिकांश कंपाइलर काम करते हैं।
14
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.