2010-08-25 9 views
6

क्या C/C++ में "constify" ऑपरेशन होना समझदारी होगी जो एक चर const बनाता है?क्या सी ++ में 'कॉन्स्टिफाइ' ऑपरेशन करने का अर्थ होगा?

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

std::vector<int> v; 
v.push_back(5); 
constify v; // now it's const 

वर्तमान में, इस तरह के एक संभावना के बिना, आप होगा अन्य चर का परिचय एक ही प्रभाव प्राप्त करने के लिए: (? या swap का उपयोग)

std::vector<int> v0; 
v0.push_back(5); 
const std::vector<int>& v = v0; 

कि भ्रामक है क्योंकि यह गुंजाइश में एक नया नाम कहते हैं और आप इसे एक संदर्भ पूरे वेक्टर कॉपी करने से बचने के लिए बनाने के लिए की जरूरत है।

+3

क्या होता है यदि आप केवल एक शाखा में v को बनाते हैं तो? –

+1

तो शायद यह उस दायरे में केवल 'const' होना चाहिए। – Frank

+3

नहीं। यह समझ में नहीं आता है। –

उत्तर

19

सच कहूं, मैं अगर एक चर या तो const है या नहीं, अगर तुलना में इसे बदल सकते हैं भ्रामक लगता है यह कम


इस पर थोड़ा विस्तार से बता दें: कारण आप आमतौर पर इस वजह से आप एक const चर मनचाहे तरीके से शुरू नहीं किया जा सकता है करना चाहते हैं। std::vector इसका एक अच्छा उदाहरण है।

const std::vector<int> cvi = { 1, 2, 3, 4, 5, 42 }; 

हाथ में है, और भी प्रकार है कि इस आरंभीकरण वाक्य रचना की अनुमति नहीं देने के साथ हालांकि, यहां तक ​​सी ++ 1x 'सामान के बिना, आप कर सकते हैं: ठीक है, एक बार के लिए, अगले मानक एक सार्वभौमिक प्रारंभ वाक्य रचना कि यह संभव बनाता है का परिचय हमेशा एक सहायक समारोह बनाने तुम क्या चाहते करने के लिए:

const std::vector<int>& cvi = create_my_vector(); 

या, यदि आप कल्पना होना चाहते हैं:

const std::vector<int>& cvi = compile_time_list<1,2,3,4,5,42>::create_vector(); 

नोट &। फ़ंक्शन कॉल के परिणाम की प्रतिलिपि बनाने में कोई बात नहीं है, क्योंकि const संदर्भ में एक रावल्यू बाध्यकारी संदर्भ के जीवनकाल के अंत तक अपना जीवनकाल बढ़ाता है।
बेशक, सी ++ 1 एक्स 'मूव सेमेन्टिक्स का समर्थन करने वाले एक कंपाइलर के साथ पुन: संकलन करना इस तरह के अनुकूलन को बहुत अधिक आवश्यक प्रदान करेगा। लेकिन एक const संदर्भ में एक rvlaue बाध्यकारी अभी भी एक वेक्टर को स्थानांतरित करने से तेज हो सकता है और धीमी होने की संभावना नहीं है।
सी ++ 1x के साथ, आप इसे एक फ्लाई करने वाले लैम्ब्डा फ़ंक्शन भी बना सकते हैं। सी ++ सिर्फ औजारों का एक अविश्वसनीय रूप से विशाल शस्त्रागार प्रदान करता है। आईएमई, कोई फर्क नहीं पड़ता कि आपने कितना मुश्किल सोचा है, किसी और को एक ही काम करने के लिए एक और विचार के साथ आना चाहिए। और अक्सर आपके से बेहतर एक।


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

int main(int argc, char* argv[]) 
{ 
    std::istream* istrm = NULL; 
    std::ifstream ifs; 
    if(argc > 1) 
    { 
    ifs.open(argv[1]); 
    if(ifs.good()) 
     istrm = &ifs; 
    } 
    if(!istrm) 
    istrm = &std::cin; 

    while(istrm->good()) 
    { 
    // reading from *istrm implemented here 
    } 
    return 0; 
} 

के बजाय सिर्फ और 1 में चिंताओं को विभाजित) पता लगाना है, जहां से पढ़ने के लिए 2) वास्तविक पढ़ने:

int read(std::istream& is) 
{ 
    while(is.good()) 
    { 
    // reading from is implemented here 
    } 
    return 0; 
} 

int main(int argc, char* argv[]) 
{ 
    if(argc > 1) 
    { 
    std::ifstream ifs(argv[1]); 
    if(ifs.good()) 
     return read(ifs); 
    } 
    return read(std::cin); 
} 

मैं अभी तक एक चर के एक वास्तविक दुनिया उदाहरण देखना है जो उतना स्थिर नहीं था जितना वह हो सकता था जिसे चिंताओं को अलग करके तय नहीं किया जा सका।

+0

+1: मुझे इस बात से सहमत होना है - तथ्य यह है कि 'const' ctor या dtor के निष्पादन के दौरान लागू नहीं होता है, पहले से ही पर्याप्त भ्रम का कारण बनता है। –

+0

मुझे लगता है कि आपका मतलब सी ++ 0x है, सी ++ 1x नहीं। http://www2.research.att.com/~bs/C++0xFAQ.html – Ben

+0

@ बेन: इस वर्ष सी ++ 0x के लिए समय सीमा समाप्त होती है और इस साल एक नया मानक नहीं होगा। यह अगले साल बाहर आने की संभावना है, जो इसे सी ++ 11 बना देगा। यदि और देरी हो रही है, तो यह सी ++ 12 होगी। इसलिए सी ++ 1 एक्स। – sbi

6

यह एक समारोह

#include <vector> 

std::vector<int> makeVector() 
{ 
    std::vector<int> returnValue; 
    returnValue.push_back(5); 
    return returnValue; 
} 

int main() 
{ 
    const std::vector<int> myVector = makeVector(); 
} 
+2

आपके समाधान में विभिन्न अर्थशास्त्र हैं। ओपी 1 'वेक्टर ' बनाना चाहता है और पहचानकर्ता के कॉन्स्ट गैर-कॉन्स्ट को बदलना चाहता है। आपका समाधान 'वेक्टर ' की 2 प्रतियां बनाता है, – JaredPar

+6

@ जेरेड के 2 अलग-अलग लीवरों के साथ: मैं इसे इस प्रकार पढ़ता हूं: ओपी अनिवार्य रूप से निरंतर वेक्टर बनाना चाहता है, लेकिन सी ++ 0x की प्रारंभिक सूचियों की कमी के कारण नहीं हो सकता , या एक ही समय में सभी डेटा नहीं है। – Bill

+8

@ जेरेड: यह संस्करण संभवतः केवल एक वेक्टर (नामित रिटर्न वैल्यू ऑप्टिमाइज़ेशन के कारण) बना देगा, जिसे आप एक दायरे में संशोधित कर सकते हैं और फिर ओपी वांछित होने पर यह दूसरे में स्थिर हो जाता है। – UncleBens

8

आप मूल रूप से एक निर्माता के प्रभाव पुन: पेश करने की कोशिश कर रहे उपयोग करने के लिए एक महान समय है - यानी, const लागू होता है के बाद ही निर्माता पूरा करता है (और केवल जब तक dtor है लागू)। इस प्रकार, आपको जो चाहिए वह एक और वर्ग है जो आपके वेक्टर को लपेटता है और इसे सीटीआर में शुरू करता है। एक बार ctor पूरा हो जाता है और लौटाता है, उदाहरण const बन जाता है (मान लीजिए कि यह const होने के लिए परिभाषित किया गया था)।

सी ++ 0x इस तरह की रैपिंग के लिए आवश्यकता को कम करेगा। आप वेक्टर के लिए एक ऑपरेशन में वेक्टर बनाने/प्रारंभ करने के लिए ब्रेस प्रारंभकर्ताओं का उपयोग करने में सक्षम होंगे। अन्य प्रकार (कम से कम संभावित रूप से) उपयोगकर्ता द्वारा परिभाषित प्रारंभकर्ताओं को लगभग एक ही चीज़ को पूरा करने के लिए समर्थन करेंगे।

7

सी ++ स्थिर रूप से टाइप किया गया है। मेरे लिए, इस तरह के एक ऑपरेशन का परिचय इस प्रतिमान का उल्लंघन होगा और बहुत भ्रम पैदा करेगा।

+2

यह अभी भी स्थैतिक टाइपिंग है - कंपाइलर अनुमान लगा सकता है जब परिवर्तनीय समय संकलित होता है या नहीं। – liori

+3

@ लिओरी: शायद। यदि निष्पादन के दौरान एक चर का प्रकार बदल सकता है, तो इसका मतलब है कि हमें स्थिर टाइपिंग करने के लिए निष्पादन-पथ विश्लेषण करने की आवश्यकता है। यह सभी मामलों में काम नहीं करेगा, और दूसरों में आश्चर्यजनक परिणाम देगा। –

+0

आह, हाँ, उस के बारे में भूल गए। +1। – liori

3

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

आपका विचार नीचे उबलता है "मैं इसे प्रारंभ करने के बाद केवल एक चर पढ़ने के लिए कैसे बना सकता हूं?"। आप अपने चर को एक वर्ग के निजी सदस्य बनाकर एक ही प्रभाव प्राप्त कर सकते हैं, जिसे कन्स्ट्रक्टर में प्रारंभ किया गया है, और जिसके लिए आप एक गेटर प्रदान करते हैं लेकिन कोई सेटटर नहीं है।

3

यह पहले ही उल्लेख किया गया है कि C++ 0x ब्रेस-initialisers साथ कुछ हद तक इस को हल करती है:

const std::vector<int> values{1, 2, 3, 4, 5}; 

हालांकि यह केवल initialisation के लिए अनुमति देता है, और अनुमति नहीं है, उदाहरण के लिए, के बाद गैर const सदस्य कार्यों लागू निर्माता चला गया है। यह इस प्रकार एक मैक्रो constify परिभाषित करने के लिए संभव है:

#define constify(type, id) \ 
for (type const& id##_const(id), & id(id##_const), \ 
    * constify_index = &id; constify_index; constify_index = 0) 

कौन इतना की तरह इस्तेमाल किया जा सकता है:

std::vector<int> v; 

// v is non-const here. 

constify (std::vector<int>, v) { 

    // v is const here. 

} 

यह एक for पाश है कि निम्नलिखित बयान या ब्लॉक कार्यान्वित की स्थापना करके काम करता है केवल एक बार, लूप बॉडी के लिए कॉन्स्टिफाइड वेरिएबल स्थानीय के साथ। सहायक चर i_const की घोषणा नोट स्थानीय i से पहले: बयान int const& i(i) initialises iही — कि है, एक uninitialised मूल्य — और हम (i) पहले i घोषित करने के बजाय उल्लेख करने के लिए चाहते हैं, इसलिए एक अतिरिक्त स्तर जरूरत है।

आप C++ 0x सुविधाओं का उपयोग कर सकते हैं, तो decltype कीवर्ड, काम में आता आप constify के आमंत्रण से प्रकार छोड़ करने की इजाजत दी:

#define constify(id) \ 
for (decltype(id) const& id##_const(id), & id(id##_const), \ 
    * constify_index = &id; constify_index; constify_index = 0) 

आप लिखते हैं, बस की सुविधा देता है कौन सा:

constify (v) { 
    // ... 
} 

दोनों संस्करण काम करते हैं कि चर प्रारंभ में const घोषित किया गया है या नहीं। तो, हां, जो कुछ आप खोज रहे थे उसके जैसा कुछ वास्तव में संभव है, लेकिन शायद पूरी तरह से इसके लायक नहीं है।

+2

इस तरह से 'const_cast' का उपयोग अपरिभाषित व्यवहार में परिणाम। –

+0

यह सुरक्षित नहीं है, जब भी आप एक चर को 'const' के रूप में घोषित करते हैं, तो संकलक केवल पढ़ने के लिए स्मृति में सभी या हिस्से को रखना चुन सकता है, जिससे आप गलतियों को दूर कर सकते हैं और ऑब्जेक्ट को संशोधित करने का प्रयास कर सकते हैं। यह 'std :: vector' जैसी जटिल कक्षा के लिए असंभव है लेकिन अभी भी चिंता का विषय है। –

+0

ओह, ठीक है। मैंने कहा था कि यह सुरक्षित नहीं था। –

3

मुझे लगता है कि आप वैक्टर प्रारंभ करने की तुलना में कुछ अधिक सामान्य के बारे में बात कर रहे हैं (जिसे सी ++ 0x में हल किया गया है), और केवल उदाहरण के रूप में वैक्टर का उपयोग करें।

मैं नहीं बल्कि देखना यह स्थानीय कार्यों के कुछ प्रकार के माध्यम से किया चाहते हैं:

const vector<int> values = []{ 
    vector<int> v; 
    copy(some_other_data.begin(), some_other_data.end(), v); 
    sort(v); 
    return v; 
}(); 

(मैं कर सकता C++ 0x की गंदगी गुमनाम समारोह वाक्य रचना)। यह मैं काफी स्वाभाविक रूप से पढ़ सकता हूं: "यहां वर्णित दिनचर्या के अनुसार एक कॉन्स वेक्टर तैयार करें"। केवल ब्रांड्स की मात्रा मुझे थोड़ा परेशान करती है।

मैं देख सकता हूं कि सी ++ 0x प्रोग्रामर के लिए अधिक प्राकृतिक हो जाने के बाद यह कोड सी ++ मुहावरे कैसे बन सकता है।

(dehmann के सुझाव के बाद संपादित)

+1

अच्छा जवाब। और हाँ, ऐसा लगता है कि आपको '[]() {...}' में सभी कोष्ठक की आवश्यकता नहीं है। गोल कोष्ठक वैकल्पिक होते हैं, इसलिए यह '[] {...} ' – Frank

0

आप एक कक्षा में वेक्टर लपेट सकता है, लिपटे वेक्टर परिवर्तनशील का ऐलान करते हैं और उसके बाद आवरण के एक स्थिरांक उदाहरण हैं। रैपिंग क्लास वेक्टर बदल सकता है लेकिन बाहरी कॉलर्स एक कॉन्स ऑब्जेक्ट

2

वर्तमान में, const या ऐसा कुछ नहीं है जो संकलक जानता है, इसलिए संकलक एक प्रोग्राम स्वीकार नहीं करेगा जो const चर बदलने के लिए प्रयास करता है।

आप एक constify ऑपरेटर बनाना चाहते थे, तो आप (हर चर के आगे खोजशब्दों के बिना,) इस चर की संपत्ति बनाने के लिए होता है, तो यह कार्यावधि में बदल सकते हैं। और निश्चित रूप से आपको एक अपवाद फेंकना होगा जब भी कोई प्रोग्राम (वर्तमान में) const वैरिएबल को बदलने की कोशिश करता है, जिसका प्रभावी अर्थ यह है कि हर चर के लिए प्रत्येक लेखन पहुंच को const संपत्ति पहले जांचनी होगी।

यह सब सी ++ के दर्शन और हर दूसरे स्थाई रूप से टाइप की गई भाषा के खिलाफ जाता है। और यह भी मौजूदा libs के साथ बाइनरी संगतता तोड़ता है।

void foo(std::vector<int> & v) 
{ 
    v.push_back(1); 
    constify v; 
} 
void bar() { 
    std::vector<int> test(7); 
    foo(test); 
    test.clear(); 
} 

चर v foo में constified है:

2

निम्नलिखित बिट पर विचार करें? यह बार में test के समान वैरिएबल है। इस प्रकार, test.clear() कॉल अमान्य होना चाहिए। मुझे लगता है कि आप वास्तव में क्या मतलब है कि नाम "constified" है, चर नहीं।

यह निर्दिष्ट करने और कार्यान्वित करने के लिए वास्तव में छोटा होगा: constify x; एक्स नामक एक कॉन्स संदर्भ की घोषणा है, जिसमें वैरिएबल एक्स के रूप में समान आधार प्रकार है। यह सामान्य दायरे के नियमों का पालन करता है, सिवाय इसके कि इसे पिछले x घोषणा के समान दायरे में परिभाषित किया जा सकता है।

+0

बन जाता है हां, मैं मानता हूं कि यह वह नाम है जिसे संकलित किया जाएगा। यह हमेशा मौजूदा दायरे पर लागू होगा। और जैसा कि मैंने अपने प्रश्न में दिया है, वैसे ही आप 'कॉन्स्ट एंड' का उपयोग करके उसी कार्यान्वयन पर संकेत दे रहे हैं। तो हाँ, कंपाइलर को केवल 'कॉन्स्ट एंड' वैरिएबल डालने वाला यह मामूली प्रोग्राम रूपांतरण करना होगा। – Frank

+0

+1 यह दुर्भाग्यपूर्ण है कि देर से उत्तर शायद ही कभी ऊपर उठने के लिए पर्याप्त अपवॉट प्राप्त करते हैं। कई अन्य उत्तरों अच्छे हैं, लेकिन यह सबसे अच्छा हो सकता है। – thb

संबंधित मुद्दे