Cbegin / cend के पीछे क्या कारण है?


188

मुझे आश्चर्य है क्योंकि cbegin और cendC ++ 11 में पेश किए गए थे?

ऐसे मामले क्या हैं जब इन तरीकों को कॉल करने से कॉन्स्टोड के ओवरलोड से फर्क पड़ता है beginऔर end?

जवाबों:


227

यह काफी सरल है। कहो मेरे पास एक वेक्टर है:

std::vector<int> vec;

मैं इसे कुछ आंकड़ों से भरता हूं। फिर मैं इसके लिए कुछ पुनरावृत्तियों को प्राप्त करना चाहता हूं। शायद उनके आसपास से गुजर जाए। हो सकता है std::for_each:

std::for_each(vec.begin(), vec.end(), SomeFunctor());

C ++ 03 में, यह प्राप्त पैरामीटर SomeFunctorको संशोधित करने में सक्षम होने के लिए स्वतंत्र था । ज़रूर, SomeFunctorइसके पैरामीटर को मान या द्वारा ले सकता है const&, लेकिन यह सुनिश्चित करने का कोई तरीका नहीं है कि यह करता है। इस तरह मूर्खतापूर्ण कुछ किए बिना नहीं:

const std::vector<int> &vec_ref = vec;
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor());

अब, हम परिचय देते हैं cbegin/cend:

std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());

अब, हमारे पास सिंटैक्टिक आश्वासन हैं जो SomeFunctorवेक्टर के तत्वों (एक कास्ट-कास्ट के बिना, बिल्कुल) को संशोधित नहीं कर सकते हैं। हम स्पष्ट रूप से प्राप्त करते हैं const_iterator, और इसलिए SomeFunctor::operator()इसके साथ बुलाया जाएगा const int &। यदि यह इसे पैरामीटर के रूप में लेता है int &, तो C ++ कंपाइलर त्रुटि जारी करेगा।


C ++ 17 में इस समस्या का एक और अधिक सुंदर समाधान है std::as_const:। रेंज-आधारित का उपयोग करते समय, कम से कम यह सुरुचिपूर्ण है for:

for(auto &item : std::as_const(vec))

यह बस const&प्रदान की गई वस्तु के लिए एक रिटर्न देता है ।


1
मैंने सोचा था कि नया प्रोटोकॉल वकसीबेगिन () के बजाय सीबीगिन (वीसी) था।
काज ड्रैगन

20
@Kaz: std::cbegin/cendजिस तरह से std::begin/std::endमौजूद हैं, कोई मुफ्त कार्य नहीं हैं। यह समिति द्वारा एक निरीक्षण था। यदि वे कार्य मौजूद थे, तो आमतौर पर उनका उपयोग करने का तरीका होगा।
निकोल बोलस

20
जाहिर है, std::cbegin/cendC ++ 14 में जोड़ा जाएगा। देखें en.cppreference.com/w/cpp/iterator/begin
आदि Shavit

8
@ निकोलबोलस के for(auto &item : std::as_const(vec))बराबर है for(const auto &item : vec)?
लूइज़फ्लेक्स

8
@luizfls हां। आपका कोड कहता है कि आइटम को constसंदर्भ पर रखकर संशोधित नहीं किया जाएगा । निकोल के कंटेनर को कास्ट के रूप में देखा गया है, इसलिए autoएक constसंदर्भ को घटाता है । IMO auto const& itemआसान और स्पष्ट है। यह स्पष्ट नहीं है कि std::as_const()यहाँ अच्छा क्यों है; मैं देख सकता हूँ कि यह उपयोगी होगा जब कुछ गैर- constजेनेरिक कोड पास करने के लिए जहां हम उस प्रकार को नियंत्रित नहीं कर सकते हैं जो इस्तेमाल किया जाता है, लेकिन रेंज- के साथ for, हम कर सकते हैं, इसलिए ऐसा लगता है जैसे मेरे लिए वहां जोड़ा गया शोर है।
अंडरस्कोर_ड

66

निकोल बोलस ने अपने उत्तर में जो कहा, उससे परे नए autoकीवर्ड पर विचार करें :

auto iterator = container.begin();

इसके साथ auto, यह सुनिश्चित करने का कोई तरीका नहीं है कि begin()एक निरंतर ऑपरेटर को एक गैर-स्थिर कंटेनर संदर्भ के लिए लौटाया जाए। तो अब आप करते हैं:

auto const_iterator = container.cbegin();

2
@allyourcode: मदद नहीं करता। संकलक के लिए, const_iteratorबस एक और पहचानकर्ता है। न तो संस्करण सामान्य सदस्य टाइपडिफ्स की खोज का उपयोग करता है decltype(container)::iteratorया decltype(container)::const_iterator
21 दिसंबर को aschepler

2
@aschepler मुझे आपका दूसरा वाक्य समझ नहीं आ रहा है, लेकिन मुझे लगता है कि आपने मेरे सवाल में "ऑटो" के सामने "कास्ट" को याद किया। जो भी ऑटो आता है, लगता है कि const_iterator कास्ट होना चाहिए।
एलियोरकोड

26
@allyourcode: यह आपको एक इटेटर प्रदान करेगा जो स्थिर है, लेकिन यह एक आइटर से निरंतर डेटा तक भिन्न है।
19 फरवरी को aschepler

2
यह सुनिश्चित करने का एक सरल तरीका है कि आप एक const_iteratorसाथ प्राप्त करें: ऑब्जेक्ट तर्क को अर्हता प्राप्त करने के लिए autoकहा जाता है एक सहायक फ़ंक्शन टेम्पलेट लिखें make_const
कोलंबो

17
शायद मैं अब सी ++ मानसिकता में नहीं हूं, लेकिन मैं "सरल तरीके से" और "सहायक फ़ंक्शन टेम्पलेट लिखना" की अवधारणाओं के बीच संबंध नहीं देख सकता। ;)
स्टीफन माज्यूस्की

15

इसे एक व्यावहारिक usecase के रूप में लें

void SomeClass::f(const vector<int>& a) {
  auto it = someNonConstMemberVector.begin();
  ...
  it = a.begin();
  ...
}

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


8

से http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :

ताकि एक प्रोग्रामर सीधे नॉन-कॉस्ट कंटेनर से भी एक const_iterator प्राप्त कर सके

उन्होंने इसका उदाहरण दिया

vector<MyType> v;

// fill v ...
typedef vector<MyType>::iterator iter;
for( iter it = v.begin(); it != v.end(); ++it ) {
    // use *it ...
}

हालांकि, जब एक कंटेनर ट्रावर्सल केवल निरीक्षण के लिए अभिप्रेत है, तो संकलक का उपयोग करने के लिए एक आम तौर पर पसंदीदा अभ्यास है, ताकि कंपाइलर को कांस्ट-शुद्धता उल्लंघन का निदान करने की अनुमति मिल सके

नोट वर्किंग पेपर भी एडाप्टर टेम्पलेट्स का उल्लेख है, कि अब के रूप में अंतिम रूप दे दिया गया है कि std::begin()और std::end()और वह भी देशी सरणियों के साथ काम करते हैं। संबंधित std::cbegin()और std::cend()इस समय के रूप में गायब हैं, लेकिन उन्हें भी जोड़ा जा सकता है।


5

बस इस सवाल पर लड़खड़ा गया ... मुझे पता है कि यह बहुत बुरा जवाब है और यह सिर्फ एक पक्ष नोड है ...

auto const it = container.begin() फिर एक अलग प्रकार है auto it = container.cbegin()

के लिए अंतर int[5](पॉइंटर का उपयोग करके, जो मुझे पता है कि शुरुआत विधि नहीं है, लेकिन अच्छी तरह से अंतर दिखाएं ... लेकिन सी + + 14 के लिए काम करेंगे std::cbegin()और std::cend(), जो अनिवार्य रूप से एक का उपयोग करना चाहिए जब वह यहां है ...)

int numbers = array[7];
const auto it = begin(numbers); // type is int* const -> pointer is const
auto it = cbegin(numbers);      // type is int const* -> value is const

2

iteratorऔर const_iteratorइनहेरिटेंस संबंध और एक अंतर्निहित रूपांतरण तब होता है जब अन्य प्रकार के साथ तुलना या असाइन किया जाता है।

class T {} MyT1, MyT2, MyT3;
std::vector<T> MyVector = {MyT1, MyT2, MyT3};
for (std::vector<T>::const_iterator it=MyVector.begin(); it!=MyVector.end(); ++it)
{
    // ...
}

उपयोग करने cbegin()और cend()इस मामले में प्रदर्शन में वृद्धि होगी।

for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it)
{
    // ...
}

मुझे यह महसूस करने में थोड़ा समय लगा कि आपके प्रदर्शन का मतलब रूपांतरण से बचना है जब पुनरावृत्तियों की तुलना और पुनरावृत्तियों की तुलना करते हुए, लोकप्रिय मिथक नहीं है कि constमुख्य लाभ प्रदर्शन है (जो यह नहीं है: यह शब्दार्थ रूप से सही और सुरक्षित कोड है)। लेकिन, जब आपके पास एक बिंदु होता है, (ए) autoएक गैर-मुद्दा बनाता है; (बी) प्रदर्शन के बारे में बात करने में, आपको एक मुख्य बात याद आती है जो आपको यहाँ करनी चाहिए थी: लूप endके इनिट-कंडीशन में इसकी एक कॉपी घोषित करके इटरेटर को कैश करें for, और एक नई कॉपी प्राप्त करने के बजाय उसकी तुलना करें हर पुनरावृत्ति के लिए मूल्य। इससे आपकी बात बेहतर होगी। : P
underscore_d

@underscore_d constनिश्चित रूप से बेहतर प्रदर्शन को प्राप्त करने में मदद कर सकता है, न कि constकीवर्ड में कुछ जादू के कारण, बल्कि इसलिए कि कंपाइलर कुछ अनुकूलन कर सकता है यदि यह जानता है कि डेटा संशोधित नहीं किया जाएगा, जो अन्यथा संभव नहीं होगा। चेक इस बिट इस का लाइव उदाहरण के लिए एक जैसन टर्नर की बात से।
ब्रेनप्लॉट

@brainplot मैंने नहीं कहा कि यह नहीं हो सकता। मैंने कहा कि यह इसका मुख्य लाभ नहीं है और मुझे लगता है कि यह अतिरंजित हो जाता है, जब इसका वास्तविक लाभ शब्दगत रूप से सही और सुरक्षित कोड होता है।
अंडरस्कोर_ड

@underscore_d हां, मैं इस पर सहमत हूं। मैं सिर्फ यह स्पष्ट कर रहा था कि constप्रदर्शन लाभ के लिए नेतृत्व कर सकते हैं (लगभग अप्रत्यक्ष रूप से); यदि कोई व्यक्ति इसे पढ़ता है तो मुझे लगता है कि "मैं यह जोड़ने की जहमत नहीं उठाऊंगा constकि उत्पन्न कोड किसी भी तरह से प्रभावित नहीं होता है", जो कि सच नहीं है।
ब्रेनप्लॉट

0

इसकी सरल, cbegin एक निरंतर पुनरावृत्ति देता है जहाँ से एक पुनरावृत्तिक शुरू होता है

बेहतर समझ के लिए दो परिदृश्यों को यहाँ ले जाने दें

दृष्टांत 1 :

#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;

for (int i = 1; i < 6; ++i)
{
    /* code */
    v.push_back(i);
}

for(auto i = v.begin();i< v.end();i++){
    *i = *i + 5;
}

for (auto i = v.begin();i < v.end();i++){
    cout<<*i<<" ";
}

return 0;
}

यह चलेगा क्योंकि यहाँ पुनरावृत्ति i स्थिर नहीं है और इसे 5 से बढ़ाया जा सकता है

अब चलो cbegin का उपयोग करें और उन्हें लगातार चलने वाले परिदृश्य के रूप में दर्शाते हुए देखें - 2:

#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;

for (int i = 1; i < 6; ++i)
{
    /* code */
    v.push_back(i);
}

for(auto i = v.cbegin();i< v.cend();i++){
    *i = *i + 5;
}

for (auto i = v.begin();i < v.end();i++){
    cout<<*i<<" ";
}

return 0;
}

यह काम नहीं करने वाला है, क्योंकि आप कैबिन और सेंड का उपयोग करके मूल्य को अपडेट नहीं कर सकते हैं जो निरंतर पुनरावृत्त रिटर्न देता है

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