संकलन समय में एक सी स्ट्रिंग की कम्प्यूटिंग लंबाई। क्या यह वास्तव में एक अड़चन है?


94

मैं संकलन समय में एक स्ट्रिंग शाब्दिक की लंबाई की गणना करने की कोशिश कर रहा हूं। ऐसा करने के लिए मैं निम्नलिखित कोड का उपयोग कर रहा हूं:

#include <cstdio>

int constexpr length(const char* str)
{
    return *str ? 1 + length(str + 1) : 0;
}

int main()
{
    printf("%d %d", length("abcd"), length("abcdefgh"));
}

सब कुछ उम्मीद के मुताबिक काम करता है, प्रोग्राम 4 प्रिंट करता है और 8. असेंबली द्वारा उत्पन्न असेंबली कोड दिखाता है कि परिणाम संकलन समय पर गणना किए जाते हैं:

0x100000f5e:  leaq   0x35(%rip), %rdi          ; "%d %d"
0x100000f65:  movl   $0x4, %esi
0x100000f6a:  movl   $0x8, %edx
0x100000f6f:  xorl   %eax, %eax
0x100000f71:  callq  0x100000f7a               ; symbol stub for: printf

मेरा प्रश्न: क्या यह मानक द्वारा गारंटी है कि lengthफ़ंक्शन का संकलन समय का मूल्यांकन किया जाएगा?

अगर यह सच है समय संकलन के लिए स्ट्रिंग स्ट्रिंग शाब्दिक अभिकलन सिर्फ मेरे लिए खोला गया है ... उदाहरण के लिए मैं संकलन समय पर हैश की गणना कर सकता हूं और कई और ...


3
जब तक पैरामीटर एक स्थिर अभिव्यक्ति है, तब तक यह होना चाहिए।
क्रिस

1
@chris क्या इस बात की कोई गारंटी है कि एक स्थिर अभिव्यक्ति हो सकने वाली चीज का मूल्यांकन उस समय किया जाना चाहिए जब एक संदर्भ में उपयोग किया जाता है जिसमें निरंतर अभिव्यक्ति की आवश्यकता नहीं होती है?
टीसी

12
BTW, सहित <cstdio>और फिर कॉलिंग ::printfगैर-पोर्टेबल है। मानक केवल <cstdio>प्रदान करने की आवश्यकता है std::printf
बेन वोइग्ट

1
@BenVoigt ठीक है, इस ओर इशारा करने के लिए धन्यवाद :) शुरू में मैंने std :: cout का उपयोग किया, लेकिन उत्पन्न कोड वास्तविक मूल्यों को खोजने के लिए बहुत बड़ा था :)
Mircea Ispas

3
@ फेलिक्स मैं अक्सर गॉडबोल्ट का उपयोग करता हूं जब अनुकूलन से निपटने वाले प्रश्नों का उत्तर देने और उपयोग printfकरने से निपटने के लिए काफी कम कोड हो सकता है।
शफीक यघमौर

जवाबों:


76

लगातार अभिव्यक्तियों का संकलन समय पर मूल्यांकन किए जाने की गारंटी नहीं है, हमारे पास केवल C ++ मानक खंड का एक गैर-मानक उद्धरण है जो 5.19 लगातार यह कहता है:

[...]> [नोट: अनुवाद के दौरान लगातार अभिव्यक्तियों का मूल्यांकन किया जा सकता है।

आप constexprचर को परिणाम को यह सुनिश्चित करने के लिए असाइन कर सकते हैं कि इसका संकलन समय पर मूल्यांकन किया गया है, हम इसे ब्रेज़ेन स्ट्रॉस्ट्रुप के C ++ 11 संदर्भ से देख सकते हैं जो कहता है ( जोर मेरा ):

संकलित समय में अभिव्यक्तियों का मूल्यांकन करने में सक्षम होने के अलावा, हम संकलन समय पर मूल्यांकन की आवश्यकता के लिए सक्षम होना चाहते हैं ; एक चर परिभाषा के सामने constexpr करता है (और तात्पर्य const):

उदाहरण के लिए:

constexpr int len1 = length("abcd") ;

बज़्ने स्ट्रॉस्ट्रप इस सारांश का एक सारांश देता है जब हम इस isocpp ब्लॉग प्रविष्टि में संकलित समय मूल्यांकन का आश्वासन दे सकते हैं और कहते हैं:

[...] सही उत्तर - जैसा कि हर्ब द्वारा कहा गया है - यह है कि मानक के अनुसार एक संकलक कार्य का संकलन समय पर या चलाने के समय पर मूल्यांकन किया जा सकता है जब तक कि इसे एक स्थिर अभिव्यक्ति के रूप में उपयोग नहीं किया जाता है, जिस स्थिति में इसे संकलन में मूल्यांकन किया जाना चाहिए। -समय। संकलन-समय के मूल्यांकन की गारंटी देने के लिए, हमें या तो इसका उपयोग करना चाहिए जहां एक स्थिर अभिव्यक्ति की आवश्यकता होती है (उदाहरण के लिए, सरणी बाउंड या केस लेबल के रूप में) या इसका उपयोग एक कॉन्स्ट्रेक्स को इनिशियलाइज़ करने के लिए करें। मुझे उम्मीद है कि कोई भी स्वाभिमानी संकलक अनुकूलन के अवसर को याद नहीं करेगा जो मैंने मूल रूप से कहा था: "एक कॉन्स्ट्रेक्स फ़ंक्शन का संकलन समय पर मूल्यांकन किया जाता है यदि इसके सभी तर्क निरंतर अभिव्यक्ति हैं।"

तो यह दो मामलों की रूपरेखा देता है जहां इसका संकलन समय पर मूल्यांकन किया जाना चाहिए:

  1. जहां निरंतर अभिव्यक्ति की आवश्यकता होती है वहां इसका उपयोग करें, यह ड्राफ्ट मानक में कहीं भी प्रतीत होगा जहां वाक्यांश shall be ... converted constant expressionयाshall be ... constant expression उपयोग किया जाता है, जैसे कि एक सरणी बाध्य।
  2. constexprजैसा कि मैंने ऊपर उल्लिखित किया है, इसे शुरू करने के लिए इसका उपयोग करें ।

4
कहा कि, सिद्धांत रूप में एक संकलक आंतरिक या कोई लिंकेज के साथ एक वस्तु को देखने का हकदार है constexpr int x = 5;, निरीक्षण करें कि इसे संकलन-समय पर मान की आवश्यकता नहीं है (यह मानकर कि यह टेम्पलेट पैरामीटर या व्हाट्सएप के रूप में उपयोग नहीं किया जाता है) और वास्तव में उत्सर्जन कोड जो 1 और 4 अतिरिक्त ऑप्स के 5 तात्कालिक मूल्यों का उपयोग करके रनटाइम पर प्रारंभिक मूल्य की गणना करता है। एक अधिक यथार्थवादी उदाहरण: संकलक एक पुनरावृत्ति सीमा से टकरा सकता है और रनटाइम तक गणना को स्थगित कर सकता है। जब तक आप ऐसा कुछ नहीं करते हैं जो कंपाइलर को वास्तव में मूल्य का उपयोग करने के लिए मजबूर करता है, "संकलन समय पर मूल्यांकन करने की गारंटी" एक क्यूओ समस्या है।
स्टीव जेसप

@SteveJessop Bjarne एक अवधारणा का उपयोग करता हुआ प्रतीत होता है, जिसमें एक एनालॉग नहीं होता है मैं ड्राफ्ट मानक में पा सकता हूं जो अनुवाद में मूल्यांकन किए गए निरंतर अभिव्यक्ति का मतलब है। इसलिए ऐसा लगता है कि मानक स्पष्ट रूप से यह नहीं बताता है कि वह क्या कह रहा है, इसलिए मैं आपसे सहमत होना चाहूंगा। हालांकि बज़्ने और हर्ब दोनों इस पर सहमत प्रतीत होते हैं, जो यह संकेत दे सकता है कि यह केवल अंडरस्क्राइब है।
शफीक यघमौर

2
मुझे लगता है कि वे दोनों केवल "स्वाभिमानी संकलक" पर विचार कर रहे हैं, जैसा कि मानकों के अनुरूप है, लेकिन विलक्षण रूप से अवरोधक संकलक I परिकल्पना है। यह उस मानक के बारे में तर्क करने के साधन के रूप में उपयोगी है जो वास्तव में मानक की गारंटी देता है , और बहुत कुछ नहीं ;-)
स्टीव जेसप

3
@SteveJessop कुख्यात, (और दुर्भाग्य से कोई नहीं) नरक ++ की तरह विघ्नकारी संकलक। इस तरह की बात वास्तव में अनुरूपता / पोर्टेबिलिटी के परीक्षण के लिए बहुत अच्छी होगी।
Angew को अब SO

जैसे-अगर नियम के तहत, मान का उपयोग करते हुए एक संकलित संकलन समय स्थिर भी पर्याप्त नहीं है: संकलक अपने स्रोत की एक प्रति जहाज करने के लिए स्वतंत्र है और इसे रनटाइम पर फिर से जमा करना है, या प्रकार की गणना निर्धारित करने के लिए ti e गणना को बर्बाद करना है। परिवर्तनशील, या बस निरर्थक constexprबुराई से बाहर अपनी गणना फिर से चलाएँ । स्रोत की दी गई पंक्ति में प्रति सेकंड 1 वर्ण की प्रतीक्षा करना या स्रोत की दी गई पंक्ति को लेना और शतरंज की स्थिति को प्राप्त करने के लिए उसका उपयोग करना और फिर दोनों पक्षों को यह निर्धारित करना है कि कौन जीता।
यक्क - एडम नेवेरूमोंट

27

यह पता लगाना वास्तव में आसान है कि क्या constexprफ़ंक्शन के लिए कॉल का परिणाम कोर निरंतर अभिव्यक्ति है या केवल अनुकूलित किया जा रहा है:

इसे एक ऐसे संदर्भ में उपयोग करें जहां एक स्थिर अभिव्यक्ति की आवश्यकता होती है।

int main()
{
    constexpr int test_const = length("abcd");
    std::array<char,length("abcdefgh")> test_const2;
}

4
... और संकलित करें -pedantic, यदि आप gcc का उपयोग करते हैं। अन्यथा, आपको कोई चेतावनी और त्रुटियां नहीं मिलती हैं
B youови

@ B @овиЈ या इसका उपयोग ऐसे संदर्भ में करें जहां GCC के पास कोई एक्सटेंशन नहीं है जो संभावित रूप से मिल रहा है, जैसे कि एक टेम्पलेट तर्क।
Angew को अब SO

क्या कोई एनम हैक अधिक विश्वसनीय नहीं होगा? जैसे कि enum { Whatever = length("str") }?
शार्पूथ

18
उल्लेख के लायक हैstatic_assert(length("str") == 3, "");
क्रिस

8
constexpr auto test = /*...*/;शायद सबसे सामान्य और सीधा है।
टीसी

19

बस एक नोट, कि आधुनिक संकलक (जैसे gcc-4.x) strlenसंकलित समय पर स्ट्रिंग शाब्दिकों के लिए करते हैं क्योंकि यह आमतौर पर एक आंतरिक कार्य के रूप में परिभाषित किया जाता है । कोई अनुकूलन सक्षम नहीं होने के साथ। हालांकि परिणाम एक संकलन समय स्थिर नहीं है।

उदाहरण के लिए:

printf("%zu\n", strlen("abc"));

का परिणाम:

movl    $3, %esi    # strlen("abc")
movl    $.LC0, %edi # "%zu\n"
movl    $0, %eax
call    printf

ध्यान दें, यह काम करता है क्योंकि strlenएक अंतर्निहित फ़ंक्शन है, अगर हम -fno-builtinsइसे रन-टाइम पर कॉल करने के लिए इसका उपयोग करते हैं, तो इसे लाइव देखें
Shafik Yaghmour

strlenहै constexprभी साथ मेरे लिए, -fno-nonansi-builtins(तरह लगता है -fno-builtins++ किसी भी अधिक ग्राम में मौजूद नहीं है)। मैं कहता हूं कि "कॉन्स्ट्रेप", क्योंकि मैं यह कर सकता हूं template<int> void foo();और foo<strlen("hi")>(); जी + ++ - 4.8.4
एरोन मैकडैड

18

मुझे एक और फ़ंक्शन का प्रस्ताव दें जो पुनरावर्ती होने के बिना संकलन समय पर एक स्ट्रिंग की लंबाई की गणना करता है।

template< size_t N >
constexpr size_t length( char const (&)[N] )
{
  return N-1;
}

आइडोन के इस सैंपल कोड पर एक नजर डालें


4
यह एम्बेडेड '\ 0' के कारण स्ट्रलेन के बराबर नहीं हो सकता है: स्ट्रलेन ("हाय \ 0there")! = लंबाई ("हाय \ 0there")
unkulunkulu

यह सही तरीका है, यह प्रभावी आधुनिक सी ++ (यदि मैं सही ढंग से याद करता हूं) में एक उदाहरण है। हालांकि, एक अच्छा स्ट्रिंग वर्ग है जो पूरी तरह से विवश है इस उत्तर को देखें: स्कॉट शूर के str_const , शायद यह अधिक उपयोगी (और कम सी शैली) होगा।
क्वांटमर्कल

@ माइक विकर ऑप्स, यह अजीब है। : यहाँ विभिन्न लिंक कर रहे हैं सवाल के लिए लिंक , कागज के लिए लिंक , Git पर स्रोत के लिए लिंक
QuantumKarl

अब yow do: char temp[256]; sprintf(temp, "%u", 2); if(1 != length(temp)) printf("Your solution doesn't work"); ideone.com/IfKUHV
पाब्लो एरियल

7

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

संकलन के समय मूल्यांकन करने के लिए मैंने निम्नलिखित ट्रिक का उपयोग किया। दुर्भाग्य से यह केवल अभिन्न मूल्यों के साथ काम करता है (अर्थात फ्लोटिंग पॉइंट वैल्यू के साथ नहीं)।

template<typename T, T V>
struct static_eval
{
  static constexpr T value = V;
};

अब, अगर तुम लिखो

if (static_eval<int, length("hello, world")>::value > 7) { ... }

आप यह सुनिश्चित कर सकते हैं कि ifबयान एक संकलन-समय स्थिरांक है जिसमें कोई रन-टाइम ओवरहेड नहीं है।


8
या सिर्फ std :: इंटीग्रल_कंस्टैंट <int, लंबाई (...)> :: मान
Mircea Ispas

1
उदाहरण एक व्यर्थ उपयोग का एक सा है क्योंकि lenहोने का constexprमतलब lengthयह है कि वैसे भी संकलन समय पर मूल्यांकन किया जाना चाहिए।
क्रिस

@chris मैं इसे नहीं जानता था चाहिए , हो सकता है कि मैं देखा है कि यह है मेरी संकलक के साथ।
5gon12eder

ठीक है, अन्य उत्तरों के बहुमत के अनुसार, इसलिए मैंने उदाहरण को कम निरर्थक होने के लिए संशोधित किया है। वास्तव में, यह एक- ifकॉन्डिशन (जहां यह आवश्यक था कि कंपाइलर ने डेड कोड एलिमिनेशन किया था) जिसके लिए मैंने मूल रूप से ट्रिक का उपयोग किया था।
5gon12eder

1

सामान्यीकृत निरंतर अभिव्यक्तियों पर विकिपीडिया के प्रवेश से एक संक्षिप्त व्याख्या :

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

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


इन शर्तों की गारंटी नहीं है कि लौटा मूल्य स्थिर है । उदाहरण के लिए, फ़ंक्शन को अन्य तर्क मानों के साथ बुलाया जा सकता है।
बेन वोइग्ट

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