क्या निर्धारित करता है कि एक कॉन्स्ट्रेक्स फ़ंक्शन एक स्थिर अभिव्यक्ति है?
(संकलक का उपयोग c ++ 17 के साथ किया गया है जहाँ तक मुझे पता है (दृश्य स्टूडियो में इसे खोजना मुश्किल है)
#include <iostream>
using namespace std;
void increment( int& v )
{
++v;
}
int constexpr f()
{
int v = 0;
increment( v );
return v;
}
int main( )
{
cout << f( ) << '\n';
}
उपरोक्त कोड संकलन पर त्रुटि देता है:
constexpr फ़ंक्शन 'f' एक स्थिर अभिव्यक्ति में परिणाम नहीं कर सकता है।
जैसा कि मैं समझता हूं कि यह इसलिए है क्योंकि फ़ंक्शन increment
कोई बाधा नहीं है। मुझे क्या भ्रमित करता है कि निम्नलिखित कोड ठीक संकलित करता है:
#include <iostream>
using namespace std;
void increment( int& v )
{
++v;
}
int constexpr f()
{
int v = 0;
for( int i = 0; i < 1; ++i )
{
increment( v );
}
return v;
}
int main( )
{
cout << f( ) << '\n';
}
यह कोड कार्यात्मक रूप से समान है और यह संकलन करता है, हालांकि वेतन वृद्धि अभी भी एक बाधा नहीं है। मुझे समझ नहीं आता कि यह कैसे संभव है कि रेंज [0, 1) के माध्यम से एक लूप संकलक को यह महसूस करने का कारण बनता है कि फ़ंक्शन f
वास्तव में एक बाधा है।
अगर कोई c ++ और इस स्पष्ट असंगतता में कमी पर कुछ अंतर्दृष्टि दे सकता है, तो मैं इसकी बहुत सराहना करूंगा।
जवाब
दोनों कार्यक्रम "प्रति-निदान रहित आवश्यक नहीं" हैं, प्रति [dcl.constexpr] / 6 :
एक कॉन्स्ट्रेक्स फ़ंक्शन या कॉन्स्टैक्स निर्माण के लिए जो न तो डिफ़ॉल्ट है और न ही कोई टेम्प्लेट है, यदि कोई तर्क मान मौजूद नहीं है, तो फ़ंक्शन या कंस्ट्रक्टर का एक आमंत्रण मुख्य स्थिर अभिव्यक्ति का मूल्यांकन किया जा सकता है, या, एक कंस्ट्रक्टर के लिए, एक मूल्यांकन किया गया सब-एक्सप्रेशन कुछ स्थिर-आरंभीकृत ऑब्जेक्ट ( [basic.start.static] ) की प्रारंभिक-पूर्ण अभिव्यक्ति , प्रोग्राम बीमार है, किसी भी नैदानिक की आवश्यकता नहीं है।
यह थोड़ा अजीब है कि जीसीसी सिर्फ दूसरे कार्यक्रम के साथ इस मुद्दे को नोटिस करने में विफल रहता है, लेकिन यह अभी भी अनुरूप है।
नोट एक नैदानिक की आवश्यकता होगी यदि f
एक संदर्भ में उपयोग किया जाता है जो वास्तव में उदाहरण के लिए एक स्थिर अभिव्यक्ति की आवश्यकता होती है constexpr int n = f();
।
कुछ चीजों को एक कॉन्स्ट्रेक्स फ़ंक्शन में अनुमति नहीं है। इनको डायग्नोस्टिक (आमतौर पर एक त्रुटि संदेश) की आवश्यकता होती है, भले ही फ़ंक्शन का उपयोग लगातार अभिव्यक्ति में कभी नहीं किया गया हो - बिशेन का जवाब देखें । लेकिन प्रश्न के कार्यक्रम इन कड़े नियमों का उल्लंघन नहीं करते हैं।
चूंकि आप f
एक स्थिर अभिव्यक्ति में नहीं बुला रहे हैं , इसलिए आपका प्रश्न पूछ रहा है कि क्या संकलक को निदान करने की आवश्यकताf
है, जिसे एक स्थिर अभिव्यक्ति में नहीं कहा जा सकता है, केवल इसकी परिभाषा पर आधारित है ।
किसी फ़ंक्शन की परिभाषा पर आवश्यकताओं को यहाँ परconstexpr
एन्यूमरेट किया गया है :
एक constexpr फ़ंक्शन की परिभाषा निम्नलिखित आवश्यकताओं को पूरा करेगी:
(३.१) इसका वापसी प्रकार (यदि कोई हो) एक शाब्दिक प्रकार होगा;
(३.२) इसके प्रत्येक पैरामीटर प्रकार एक शाब्दिक प्रकार होंगे;
(३.३) यह एक धनावेश नहीं होगा;
(३.४) यदि फ़ंक्शन एक निर्माता या विध्वंसक है, तो उसके वर्ग में कोई आभासी आधार वर्ग नहीं होगा;
(३.५) इसका कार्य-शरीर संलग्न नहीं होगा
(3.5.1) गोटो स्टेटमेंट,
(3.5.2) एक पहचानकर्ता लेबल,
(3.5.3) गैर-शाब्दिक प्रकार या स्थिर या थ्रेड भंडारण अवधि की एक चर की परिभाषा।
जैसा कि देखा जा सकता है, f
सूची में किसी भी आवश्यकता का उल्लंघन नहीं करता है। यदि यह निदान नहीं करता है तो एक संकलक अनुरूप है।
जैसा कि एशप्लर के उत्तर में कहा गया है , ऐसे constexpr
कार्यों f
को एक स्थिर अभिव्यक्ति में नहीं कहा जा सकता है, लेकिन वे इस तरह से निदान करने योग्य नहीं हैं, जिन्हें बीमार-गठन-निदान-आवश्यक माना जाता है।
आप वास्तव f
में संकलन समय पर "कॉलिंग" नहीं कर रहे हैं ।
यदि आपका मुख्य कार्य शामिल है: static_assert(f() == 1, "f() returned 1");
मुझे संदेह है कि आपको "एफ () एक स्थिर अभिव्यक्ति नहीं है" त्रुटि होगी।
यहाँ एक संबंधित प्रश्न है
मानक के लिए आवश्यक है कि एक constexpr
फ़ंक्शन वास्तव में कुछ सेट मापदंडों के लिए संकलन समय पर मूल्यांकन योग्य हो लेकिन सभी नहीं। यह constexpr
कुछ कार्यों को करने वाले फ़ंक्शन का निदान करने के लिए संकलक की आवश्यकता नहीं है जो कुछ परिस्थितियों में गैर-संकलन-समय हो सकता है, या यहां तक कि इस तरह के फ़ंक्शन में मापदंडों का ऐसा सेट है। यह उन्हें रोकने की समस्या को हल करने से बचा जाता है।