आइए कंटेनर में तत्वों को देखने के बीच अंतर करना शुरू करें। उन्हें जगह में संशोधित करना।
तत्वों का अवलोकन करना
आइए एक सरल उदाहरण पर विचार करें:
vector<int> v = {1, 3, 5, 7, 9};
for (auto x : v)
cout << x << ' ';
उपरोक्त कोड निम्नलिखित तत्वों को प्रिंट int
करता है vector
:
1 3 5 7 9
अब एक और मामले पर विचार करें, जिसमें वेक्टर तत्व केवल सरल पूर्णांक नहीं हैं, बल्कि एक और अधिक जटिल वर्ग के उदाहरण हैं, जिसमें कस्टम कॉपी सेंसर आदि हैं।
// A sample test class, with custom copy semantics.
class X
{
public:
X()
: m_data(0)
{}
X(int data)
: m_data(data)
{}
~X()
{}
X(const X& other)
: m_data(other.m_data)
{ cout << "X copy ctor.\n"; }
X& operator=(const X& other)
{
m_data = other.m_data;
cout << "X copy assign.\n";
return *this;
}
int Get() const
{
return m_data;
}
private:
int m_data;
};
ostream& operator<<(ostream& os, const X& x)
{
os << x.Get();
return os;
}
यदि हम for (auto x : v) {...}
इस नए वर्ग के साथ उपरोक्त सिंटैक्स का उपयोग करते हैं :
vector<X> v = {1, 3, 5, 7, 9};
cout << "\nElements:\n";
for (auto x : v)
{
cout << x << ' ';
}
आउटपुट कुछ इस तरह है:
[... copy constructor calls for vector<X> initialization ...]
Elements:
X copy ctor.
1 X copy ctor.
3 X copy ctor.
5 X copy ctor.
7 X copy ctor.
9
जैसा कि इसे आउटपुट से पढ़ा जा सकता है, लूप पुनरावृत्तियों के लिए रेंज-आधारित के दौरान कॉपी कंस्ट्रक्टर कॉल किए जाते हैं।
ऐसा इसलिए है क्योंकि हम कंटेनर के तत्वों को मूल्य
( भाग ) द्वारा कैप्चर कर रहे हैं ।auto x
for (auto x : v)
यह अक्षम कोड है, उदाहरण के लिए, यदि ये तत्व हैं, तो std::string
ढेर मेमोरी आवंटन हो सकते हैं, मेमोरी मैनेजर की महंगी यात्राओं के साथ, आदि। यह बेकार है अगर हम सिर्फ एक कंटेनर में तत्वों का निरीक्षण करना चाहते हैं ।
इसलिए, एक बेहतर सिंटैक्स उपलब्ध है: संदर्भ द्वाराconst
कैप्चर करें , अर्थात const auto&
:
vector<X> v = {1, 3, 5, 7, 9};
cout << "\nElements:\n";
for (const auto& x : v)
{
cout << x << ' ';
}
अब आउटपुट है:
[... copy constructor calls for vector<X> initialization ...]
Elements:
1 3 5 7 9
बिना किसी स्प्रिचुअल (और संभावित रूप से महंगे) के कंस्ट्रक्टर कॉल को कॉपी करें।
तो, जब अवलोकन एक कंटेनर (यानी, केवल पढ़ने के लिए पहुँच के लिए) में तत्वों, निम्न सिंटैक्स सरल के लिए ठीक है सस्ता करने के लिए प्रतिलिपि प्रकार, जैसे int
, double
, आदि .:
for (auto elem : container)
बेकार, const
संदर्भ द्वारा कैप्चर करना सामान्य मामले में बेहतर है , बेकार (और संभावित रूप से महंगी) कॉपी कंस्ट्रक्टर कॉल से बचने के लिए:
for (const auto& elem : container)
कंटेनर में तत्वों को संशोधित करना
यदि हम श्रेणी-आधारित का उपयोग करके कंटेनर में तत्वों को संशोधित करना चाहते हैं for
, तो उपरोक्त for (auto elem : container)
और for (const auto& elem : container)
वाक्यविन्यास गलत हैं।
वास्तव में, पूर्व मामले में, मूल तत्व की elem
एक प्रति संग्रहीत करता है , इसलिए इसमें किए गए संशोधन बस खो जाते हैं और कंटेनर में लगातार संग्रहीत नहीं होते हैं, जैसे:
vector<int> v = {1, 3, 5, 7, 9};
for (auto x : v) // <-- capture by value (copy)
x *= 10; // <-- a local temporary copy ("x") is modified,
// *not* the original vector element.
for (auto x : v)
cout << x << ' ';
उत्पादन सिर्फ प्रारंभिक अनुक्रम है:
1 3 5 7 9
इसके बजाय, for (const auto& x : v)
केवल उपयोग करने का प्रयास संकलित करने में विफल रहता है।
g ++ इस तरह से एक त्रुटि संदेश आउटपुट करता है:
TestRangeFor.cpp:138:11: error: assignment of read-only reference 'x'
x *= 10;
^
इस मामले में सही दृष्टिकोण गैर- const
संदर्भ द्वारा कैप्चरिंग है :
vector<int> v = {1, 3, 5, 7, 9};
for (auto& x : v)
x *= 10;
for (auto x : v)
cout << x << ' ';
आउटपुट है (उम्मीद के मुताबिक):
10 30 50 70 90
यह for (auto& elem : container)
सिंटैक्स अधिक जटिल प्रकारों के लिए भी काम करता है, उदाहरण के लिए vector<string>
:
vector<string> v = {"Bob", "Jeff", "Connie"};
// Modify elements in place: use "auto &"
for (auto& x : v)
x = "Hi " + x + "!";
// Output elements (*observing* --> use "const auto&")
for (const auto& x : v)
cout << x << ' ';
आउटपुट है:
Hi Bob! Hi Jeff! Hi Connie!
प्रॉक्सी पुनरावृत्तियों का विशेष मामला
मान लें कि हमारे पास एक है vector<bool>
, और हम उपरोक्त सिंटैक्स का उपयोग करते हुए, इसके तत्वों की तार्किक बूलियन स्थिति को पलटना चाहते हैं:
vector<bool> v = {true, false, false, true};
for (auto& x : v)
x = !x;
उपरोक्त कोड संकलन करने में विफल रहता है।
g ++ इस के समान त्रुटि संदेश देता है:
TestRangeFor.cpp:168:20: error: invalid initialization of non-const reference of
type 'std::_Bit_reference&' from an rvalue of type 'std::_Bit_iterator::referen
ce {aka std::_Bit_reference}'
for (auto& x : v)
^
समस्या यह है कि std::vector
टेम्पलेट के लिए विशिष्ट है bool
, एक कार्यान्वयन के साथ जो अंतरिक्ष को अनुकूलित करने के लिए एस पैक करता bool
है (प्रत्येक बूलियन मूल्य एक बिट में संग्रहीत होता है, एक बाइट में आठ "बूलियन" बिट्स)।
उसके कारण (चूंकि किसी एक बिट के संदर्भ को वापस करना संभव नहीं है),
vector<bool>
तथाकथित "प्रॉक्सी इट्रेटर" पैटर्न का उपयोग करता है । एक "प्रॉक्सी इट्रेटर" एक ऐसा इटरेटर है, जो डिरेल्ड होने पर, एक साधारण उपज नहींbool &
देता है, बल्कि एक अस्थायी ऑब्जेक्ट को लौटाता है (मान द्वारा) , जो एक प्रॉक्सी क्लास कन्वर्टिबल हैbool
। ( स्टैकऑवरफ्लो पर यहां भी देखें यह सवाल और संबंधित जवाब ।)
के तत्वों को संशोधित करने के लिए vector<bool>
, एक नए प्रकार के सिंटैक्स (उपयोग auto&&
) का उपयोग किया जाना चाहिए:
for (auto&& x : v)
x = !x;
निम्नलिखित कोड ठीक काम करता है:
vector<bool> v = {true, false, false, true};
// Invert boolean status
for (auto&& x : v) // <-- note use of "auto&&" for proxy iterators
x = !x;
// Print new element values
cout << boolalpha;
for (const auto& x : v)
cout << x << ' ';
और आउटपुट:
false true true false
ध्यान दें कि for (auto&& elem : container)
वाक्यविन्यास साधारण (गैर-छद्म) पुनरावृत्तियों (उदाहरण के लिए vector<int>
या a vector<string>
) के अन्य मामलों में भी काम करता है ।
(एक साइड नोट के रूप में, उक्त " for (const auto& elem : container)
छद्म कार्य " वाक्यविन्यास ठीक छद्म पुनरावृत्त मामले के लिए भी।)
सारांश
उपरोक्त चर्चा को निम्नलिखित दिशानिर्देशों में संक्षेपित किया जा सकता है:
तत्वों के अवलोकन के लिए, निम्नलिखित सिंटैक्स का उपयोग करें:
for (const auto& elem : container) // capture by const reference
यदि वस्तुओं को कॉपी करना सस्ता है (जैसे int
s, double
s, आदि), तो थोड़ा सा सरलीकृत रूप का उपयोग करना संभव है:
for (auto elem : container) // capture by value
के लिए संशोधित जगह, उपयोग में तत्वों:
for (auto& elem : container) // capture by (non-const) reference
यदि कंटेनर "प्रॉक्सी पुनरावृत्तियों" (जैसे std::vector<bool>
) का उपयोग करता है, तो उपयोग करें:
for (auto&& elem : container) // capture by &&
बेशक, अगर लूप बॉडी के अंदर तत्व की एक स्थानीय प्रतिलिपि बनाने की आवश्यकता है, तो मूल्य द्वारा कैप्चर करना ( for (auto elem : container)
) एक अच्छा विकल्प है।
जेनेरिक कोड पर अतिरिक्त नोट्स
में सामान्य कोड , क्योंकि हम सामान्य प्रकार के बारे में मान्यताओं नहीं कर सकते हैं T
प्रतिलिपि के लिए सस्ती जा रहा है, में अवलोकन मोड यह हमेशा उपयोग करने के लिए सुरक्षित है for (const auto& elem : container)
।
(यह संभावित रूप से महंगी बेकार प्रतियों को ट्रिगर नहीं करेगा, सस्ते-टू-कॉपी प्रकारों के लिए भी ठीक काम करेगा int
, और प्रॉक्सी-पुनरावृत्तियों का उपयोग करने वाले कंटेनरों के लिए भी, जैसे std::vector<bool>
।)
इसके अलावा, संशोधित मोड में, अगर हम प्रॉक्सी-पुनरावृत्तियों के मामले में भी सामान्य कोड काम करना चाहते हैं , तो सबसे अच्छा विकल्प है for (auto&& elem : container)
।
(यह साधारण गैर-प्रॉक्सी-पुनरावृत्तियों का उपयोग करने वाले कंटेनरों के लिए भी ठीक काम करेगा, जैसे std::vector<int>
या std::vector<string>
।)
तो, सामान्य कोड में , निम्नलिखित दिशानिर्देश दिए जा सकते हैं:
तत्वों के अवलोकन के लिए, उपयोग करें:
for (const auto& elem : container)
के लिए संशोधित जगह, उपयोग में तत्वों:
for (auto&& elem : container)