खोज…
परिचय
constexpr
एक ऐसा कीवर्ड है जिसका उपयोग चर के मान को स्थिर अभिव्यक्ति के रूप में चिह्नित करने के लिए किया जा सकता है, एक ऐसा कार्य जो निरंतर अभिव्यक्ति में संभावित रूप से उपयोग किया जा सकता है, या (C ++ 17 के बाद से) यदि एक बयान के रूप में संकलित होने के लिए चयनित इसकी केवल एक शाखा है।
टिप्पणियों
C ++ 11 में constexpr
कीवर्ड जोड़ा गया था, लेकिन कुछ वर्षों से C ++ 11 मानक प्रकाशित होने के बाद, सभी प्रमुख संकलकों ने इसका समर्थन नहीं किया। उस समय जब C ++ 11 मानक प्रकाशित हुआ था। C ++ 14 के प्रकाशन के समय तक, सभी प्रमुख कंपाइलर constexpr
समर्थन constexpr
।
constexpr चरों
एक चर घोषित constexpr
परोक्ष है const
और अपने मूल्य के लिए एक निरंतर अभिव्यक्ति के रूप में इस्तेमाल किया जा सकता है।
#define
साथ तुलना
एक constexpr
#define
आधारित संकलन-समय अभिव्यक्तियों के लिए टाइप-सुरक्षित प्रतिस्थापन है। constexpr
के साथ संकलन-समय मूल्यांकन अभिव्यक्ति को परिणाम के साथ बदल दिया जाता है। उदाहरण के लिए:
int main()
{
constexpr int N = 10 + 2;
cout << N;
}
निम्नलिखित कोड का उत्पादन करेगा:
cout << 12;
एक प्री-प्रोसेसर आधारित संकलन-समय मैक्रो अलग होगा। विचार करें:
#define N 10 + 2
int main()
{
cout << N;
}
उत्पादन करेंगे:
cout << 10 + 2;
जो स्पष्ट रूप से cout << 10 + 2;
परिवर्तित हो जाएगा cout << 10 + 2;
। हालांकि, कंपाइलर को अधिक काम करना होगा। इसके अलावा, यह एक समस्या पैदा करता है अगर सही तरीके से उपयोग नहीं किया जाता है।
उदाहरण के लिए ( #define
साथ):
cout << N * 2;
रूपों:
cout << 10 + 2 * 2; // 14
लेकिन पहले से मूल्यांकन किया गया constexpr
सही ढंग से 24
।
के साथ तुलना const
एक const
वैरिएबल एक वैरिएबल है, जिसे स्टोरेज के लिए मेमोरी की जरूरत होती है। एक constexpr
नहीं है। एक constexpr
संकलित समय का निरंतर उत्पादन करता है, जिसे बदला नहीं जा सकता है। आप तर्क दे सकते हैं कि const
को भी नहीं बदला जा सकता है। पर विचार करें:
int main()
{
const int size1 = 10;
const int size2 = abs(10);
int arr_one[size1];
int arr_two[size2];
}
अधिकांश संकलक के साथ दूसरा कथन विफल हो जाएगा (उदाहरण के लिए, जीसीसी के साथ काम कर सकते हैं)। किसी भी सरणी का आकार, जैसा कि आप जानते हैं, एक स्थिर अभिव्यक्ति होना चाहिए (यानी संकलन-समय मूल्य में परिणाम)। दूसरे चर size2
को कुछ मान दिया गया है जो रनटाइम पर तय किया गया है (भले ही आप जानते हैं कि यह 10
, संकलक के लिए यह संकलन-समय नहीं है)।
इसका मतलब यह है कि एक const
सही संकलन-समय स्थिर हो सकता है या नहीं। आप गारंटी नहीं दे सकते या लागू नहीं कर सकते हैं कि एक विशेष const
वैल्यू बिल्कुल संकलन-समय है। आप #define
उपयोग कर सकते हैं, लेकिन इसके अपने नुकसान हैं।
इसलिए बस उपयोग करें:
int main()
{
constexpr int size = 10;
int arr[size];
}
एक constexpr
अभिव्यक्ति को एक संकलन-समय मूल्य का मूल्यांकन करना चाहिए। इस प्रकार, आप उपयोग नहीं कर सकते हैं:
constexpr int size = abs(10);
जब तक समारोह ( abs
) है अपने आप में एक लौटने constexpr
।
सभी बुनियादी प्रकारों को constexpr
साथ आरंभ किया जा सकता है।
constexpr bool FailFatal = true;
constexpr float PI = 3.14f;
constexpr char* site= "StackOverflow";
दिलचस्प है, और आसानी से, आप auto
उपयोग भी कर सकते हैं:
constexpr auto domain = ".COM"; // const char * const domain = ".COM"
constexpr auto PI = 3.14; // constexpr double
बाधा कार्य
एक फ़ंक्शन जिसे constexpr
घोषित किया constexpr
वह अंतर्निहित रूप से इनलाइन होता है और ऐसे फ़ंक्शन को कॉल करता है जो संभावित रूप से निरंतर अभिव्यक्ति देता है। उदाहरण के लिए, निम्न फ़ंक्शन, यदि निरंतर अभिव्यक्ति तर्कों के साथ कहा जाता है, तो एक स्थिर अभिव्यक्ति भी मिलती है:
constexpr int Sum(int a, int b)
{
return a + b;
}
इस प्रकार, फ़ंक्शन कॉल का परिणाम सरणी बाउंड या टेम्प्लेट तर्क के रूप में, या एक constexpr
चर को इनिशियलाइज़ करने के लिए उपयोग किया जा सकता है:
int main()
{
constexpr int S = Sum(10,20);
int Array[S];
int Array2[Sum(20,30)]; // 50 array size, compile time
}
ध्यान दें कि यदि आप फ़ंक्शन के रिटर्न प्रकार विनिर्देश से constexpr
को constexpr
, तो S
को असाइनमेंट काम नहीं करेगा, क्योंकि S
एक constexpr
वेरिएबल है, और इसे एक संकलन-समय constexpr
सौंपा जाना चाहिए। इसी तरह, सरणी का आकार भी नहीं एक निरंतर अभिव्यक्ति, हो सकता है अगर समारोह Sum
नहीं है constexpr
।
constexpr
कार्यों के बारे में दिलचस्प बात यह है कि आप इसे सामान्य कार्यों की तरह भी उपयोग कर सकते हैं:
int a = 20;
auto sum = Sum(a, abs(-20));
Sum
अब एक constexpr
फ़ंक्शन नहीं होगा, इसे एक सामान्य फ़ंक्शन के रूप में संकलित किया जाएगा, चर (गैर-स्थिर) तर्क ले रहा है, और गैर-निरंतर मान लौटा रहा है। आपको दो फ़ंक्शन लिखने की आवश्यकता नहीं है।
इसका अर्थ यह भी है कि यदि आप ऐसे कॉल को नॉन-कास्ट वैरिएबल में असाइन करने का प्रयास करते हैं, तो यह संकलन नहीं करेगा:
int a = 20;
constexpr auto sum = Sum(a, abs(-20));
कारण सरल है: constexpr
को केवल एक संकलन-समय स्थिर सौंपा जाना चाहिए। हालाँकि, उपरोक्त फ़ंक्शन कॉल Sum
एक गैर- constexpr
बनाता है (आर-वैल्यू नॉन- constexpr
है, लेकिन एल-वैल्यू खुद को constexpr
होने की घोषणा कर रहा है)।
constexpr
फ़ंक्शन को एक संकलन-समय स्थिरांक भी लौटना चाहिए । निम्नलिखित संकलन नहीं होगा:
constexpr int Sum(int a, int b)
{
int a1 = a; // ERROR
return a + b;
}
क्योंकि a1
एक नॉन-कॉन्स्ट्रेक्स वैरिएबल है , और फंक्शन को एक सही constexpr
फंक्शन होने से constexpr
है। यह बनाना constexpr
और यह बताए a
भी काम नहीं - के बाद से का मूल्य a
(भेजे पैरामीटर) अभी भी अभी तक ज्ञात नहीं है:
constexpr int Sum(int a, int b)
{
constexpr int a1 = a; // ERROR
..
इसके अलावा, निम्नलिखित भी संकलित नहीं करेंगे:
constexpr int Sum(int a, int b)
{
return abs(a) + b; // or abs(a) + abs(b)
}
चूंकि abs(a)
एक निरंतर अभिव्यक्ति (यहां तक कि नहीं है abs(10)
होगा काम नहीं है, क्योंकि abs
एक नहीं लौटा रहा है constexpr int
!
इस बारे में क्या?
constexpr int Abs(int v)
{
return v >= 0 ? v : -v;
}
constexpr int Sum(int a, int b)
{
return Abs(a) + b;
}
हम अपने गढ़ी Abs
समारोह जो एक है constexpr
, और के शरीर Abs
भी किसी भी नियम का उल्लंघन नहीं करती। इसके अलावा, कॉल साइट पर ( Sum
अंदर), अभिव्यक्ति एक constexpr
मूल्यांकन constexpr
। इसलिए, Sum(-10, 20)
को कॉल करने के लिए 30
परिणामस्वरूप एक संकलन-समय स्थिर अभिव्यक्ति होगी।
स्टेटिक अगर स्टेटमेंट
if constexpr
स्टेटमेंट का उपयोग सशर्त रूप से संकलित कोड के लिए किया जा सकता है। स्थिति एक स्थिर अभिव्यक्ति होनी चाहिए। चयनित शाखा को खारिज नहीं किया जाता है । किसी टेम्प्लेट के अंदर खारिज किया गया स्टेटमेंट तत्काल नहीं है। उदाहरण के लिए:
template<class T, class ... Rest>
void g(T &&p, Rest &&...rs)
{
// ... handle p
if constexpr (sizeof...(rs) > 0)
g(rs...); // never instantiated with an empty argument list
}
इसके अलावा, चर और फ़ंक्शन जो केवल खारिज किए गए बयानों के अंदर उपयोग किए जाते हैं, उन्हें परिभाषित करने की आवश्यकता नहीं होती है, और फ़ंक्शन रिटर्न प्रकार कटौती के लिए त्याग किए गए return
स्टेटमेंट का उपयोग नहीं किया जाता है।
if constexpr
से अलग है #ifdef
। #ifdef
सशर्त रूप से कोड संकलित करता है, लेकिन केवल उन परिस्थितियों पर आधारित है जिनका मूल्यांकन प्रीप्रोसेसिंग समय पर किया जा सकता है। उदाहरण के लिए, #ifdef
को टेम्पलेट पैरामीटर के मूल्य के आधार पर सशर्त रूप से संकलित कोड के लिए इस्तेमाल नहीं किया जा सकता है। दूसरी ओर, if constexpr
इस्तेमाल वाक्यगत रूप से अमान्य कोड को छोड़ने के लिए नहीं किया जा सकता है, जबकि #ifdef
कर सकता है।
if constexpr(false) {
foobar; // error; foobar has not been declared
std::vector<int> v("hello, world"); // error; no matching constructor
}