कैसे "सीमा-आधारित छोरों" के साथ काम करने के लिए मेरे कस्टम प्रकार बनाने के लिए?


252

कई लोगों की तरह इन दिनों मैं C ++ 11 में लाए जाने वाले विभिन्न विशेषताओं की कोशिश कर रहा हूं। मेरे पसंदीदा में से एक "रेंज-आधारित छोरों" है।

मैं समझता हूँ कि:

for(Type& v : a) { ... }

के बराबर है:

for(auto iv = begin(a); iv != end(a); ++iv)
{
  Type& v = *iv;
  ...
}

और वह begin()बस a.begin()मानक कंटेनरों के लिए लौटता है ।

लेकिन क्या होगा अगर मैं अपने कस्टम प्रकार "लूप के लिए रेंज-आधारित" बनाना चाहता हूं ?

क्या मुझे सिर्फ विशेषज्ञ begin()और चाहिए end()?

यदि मेरा कस्टम प्रकार नामस्थान का है xml, तो मुझे परिभाषित करना चाहिए xml::begin()या std::begin()?

संक्षेप में, ऐसा करने के लिए दिशानिर्देश क्या हैं?


यह किसी सदस्य begin/endया मित्र, स्थिर या मुक्त को परिभाषित करके संभव है begin/end। बस सावधान रहें कि आप नि: शुल्क समारोह में किस नाम स्थान पर हैं: stackoverflow.com/questions/28242073/…
alfC

क्या कोई व्यक्ति फ्लोट वैल्यू रेंज के उदाहरण के साथ उत्तर दे सकता है जो कंटेनर नहीं है for( auto x : range<float>(0,TWO_PI, 0.1F) ) { ... }:। मैं उत्सुक हूं कि आप इस तथ्य के आसपास कैसे काम करते हैं कि `=operator! = ()` `को परिभाषित करना कठिन है। और *__beginइस मामले में डेरेफेरिंग ( ) के बारे में क्या ? मुझे लगता है कि यह एक महान योगदान होगा अगर कोई हमें दिखाए कि यह कैसे किया जाता है!
बिटकॉइलर

जवाबों:


183

इस दोष रिपोर्ट के समाधान में प्रश्न (और अधिकांश उत्तर) पोस्ट किए जाने के बाद से मानक बदल दिया गया है ।

for(:)अपने प्रकार पर एक पाश काम करने का तरीका Xअब दो तरीकों में से एक है:

  • सदस्य बनाएं X::begin()और X::end()कुछ ऐसा करें जो एक पुनरावृत्त की तरह काम करता है

  • एक निशुल्क फ़ंक्शन बनाएं begin(X&)और end(X&)कुछ ऐसा Xलौटाएं जो इट्रेटर की तरह काम करता हो, आपके प्रकार के समान नामस्थान में ।¹

और constविविधताओं के लिए भी ऐसा ही है । यह उन दोनों कंपाइलरों पर काम करेगा जो दोष रिपोर्ट परिवर्तनों को लागू करते हैं, और ऐसे कंपाइलर जो नहीं करते हैं।

लौटाई गई वस्तुओं को वास्तव में पुनरावृत्त होना नहीं है। for(:)पाश, सी ++ मानक के अधिकांश भागों के विपरीत, है कुछ बराबर करने के लिए विस्तार करने के लिए निर्दिष्ट :

for( range_declaration : range_expression )

हो जाता है:

{
  auto && __range = range_expression ;
  for (auto __begin = begin_expr,
            __end = end_expr;
            __begin != __end; ++__begin) {
    range_declaration = *__begin;
    loop_statement
  }
}

जहां चर के साथ शुरुआत __ही प्रदर्शनी के लिए कर रहे हैं, और begin_exprऔर end_exprजादू है कि कॉल begin/ end

आरंभ / समाप्ति वापसी मूल्य पर आवश्यकताएं सरल हैं: आपको पूर्व-अधिभार देना होगा ++, यह सुनिश्चित करना होगा कि आरंभिक अभिव्यक्तियाँ मान्य हैं, बाइनरी !=जिसका उपयोग बूलियन संदर्भ में किया जा सकता है, एकात्मक *जो कुछ ऐसा देता है जिसे आप असाइन कर सकते हैं- range_declarationऔर सार्वजनिक रूप से उजागर कर सकते हैं नाशक।

इस तरह से ऐसा करना जो एक पुनरावृत्त के साथ संगत नहीं है, शायद एक बुरा विचार है, क्योंकि सी ++ के भविष्य के पुनरावृत्तियों आपके कोड को तोड़ने के बारे में अपेक्षाकृत कैवेलियर हो सकते हैं यदि आप करते हैं।

एक तरफ के रूप में, यह यथोचित संभावना है कि मानक के भविष्य में संशोधन से end_exprभिन्न प्रकार की वापसी की अनुमति होगीbegin_expr । यह इस रूप में उपयोगी है कि यह "आलसी-अंत" मूल्यांकन की अनुमति देता है (जैसे कि शून्य-समाप्ति का पता लगाना) जो हाथ से लिखे गए सी लूप के रूप में कुशल होने के लिए अनुकूलित करना आसान है, और इसी तरह के अन्य फायदे हैं।


¹ ध्यान दें कि for(:)लूप्स किसी भी अस्थायी स्टोर में हैंauto&& चर हैं, और इसे एक लवल्यू के रूप में पास करते हैं। आप पता नहीं लगा सकते हैं कि आप एक अस्थायी (या अन्य प्रतिद्वंद्विता) से अधिक परेशान हैं; इस तरह के अधिभार को for(:)लूप द्वारा नहीं बुलाया जाएगा । N4527 से देखें [stmt.ranged] 1.2-1.3।

-या तो कॉल begin/ endविधि, या ADL- केवल नि: शुल्क समारोह की तलाश begin/ end, या C- शैली सरणी समर्थन के लिए जादू। ध्यान दें कि std::beginजब तक range_expressionकिसी प्रकार की वस्तु वापस नहीं आती है, तब तक उसे नहीं बुलाया जाता हैnamespace std या उसी पर निर्भर है।


में रेंज-फॉर एक्सप्रेशन अपडेट किया गया है

{
  auto && __range = range_expression ;
  auto __begin = begin_expr;
  auto __end = end_expr;
  for (;__begin != __end; ++__begin) {
    range_declaration = *__begin;
    loop_statement
  }
}

के प्रकार के साथ __beginऔर __endविघटित हो गए हैं।

यह शुरू होने के समान अंत इटेटर को अनुमति नहीं देता है। आपका अंतिम पुनरावृत्ति प्रकार एक "प्रहरी" हो सकता है जो केवल !=आरंभिक पुनरावृत्ति प्रकार के साथ समर्थन करता है ।

यह क्यों उपयोगी है, इसका एक व्यावहारिक उदाहरण यह है कि आपका एंड इट्रेटर पढ़ सकता है " char*यह देखने के लिए कि क्या यह इंगित करता है '0'" जब आपके ==साथ char*। यह एक C ++ रेंज-फॉर-एक्सप्रेशन के लिए एक शून्य-टर्मिनेटेड char*बफर पर पुनरावृत्ति करते समय इष्टतम कोड उत्पन्न करने की अनुमति देता है ।

struct null_sentinal_t {
  template<class Rhs,
    std::enable_if_t<!std::is_same<Rhs, null_sentinal_t>{},int> =0
  >
  friend bool operator==(Rhs const& ptr, null_sentinal_t) {
    return !*ptr;
  }
  template<class Rhs,
    std::enable_if_t<!std::is_same<Rhs, null_sentinal_t>{},int> =0
  >
  friend bool operator!=(Rhs const& ptr, null_sentinal_t) {
    return !(ptr==null_sentinal_t{});
  }
  template<class Lhs,
    std::enable_if_t<!std::is_same<Lhs, null_sentinal_t>{},int> =0
  >
  friend bool operator==(null_sentinal_t, Lhs const& ptr) {
    return !*ptr;
  }
  template<class Lhs,
    std::enable_if_t<!std::is_same<Lhs, null_sentinal_t>{},int> =0
  >
  friend bool operator!=(null_sentinal_t, Lhs const& ptr) {
    return !(null_sentinal_t{}==ptr);
  }
  friend bool operator==(null_sentinal_t, null_sentinal_t) {
    return true;
  }
  friend bool operator!=(null_sentinal_t, null_sentinal_t) {
    return false;
  }
};

पूर्ण C ++ 17 समर्थन के बिना एक संकलक में लाइव उदाहरण ; forलूप मैन्युअल रूप से विस्तारित।


यदि किसी भिन्न लुकअप मेकेनिज्म का उपयोग करने के लिए रेंज-आधारित है, तो हो सकता है कि सामान्य कोड में उपलब्ध की तुलना में अलग-अलग जोड़ी beginऔर endफ़ंक्शंस प्राप्त करने के लिए उस रेंज-आधारित की व्यवस्था करना संभव हो । शायद तब वे अलग तरह से व्यवहार करने के लिए बहुत विशिष्ट हो सकते हैं (यानी अधिकतम अनुकूलन प्राप्त करने के लिए अंतिम तर्क की अनदेखी करके तेजी से।) लेकिन मैं नामस्थान के साथ काफी अच्छा नहीं हूं यह सुनिश्चित करने के लिए कि यह कैसे करना है।
हारून मैकडैड

@AaronMcDaid बहुत व्यावहारिक नहीं है। आप आसानी से आश्चर्यजनक परिणामों के साथ समाप्त हो जाएंगे, क्योंकि कॉलिंग के कुछ साधन शुरू / समाप्ति रेंज के साथ शुरू / अंत के लिए समाप्त होंगे, और अन्य नहीं होंगे। सहज परिवर्तन (क्लाइंट की ओर से) व्यवहार परिवर्तन प्राप्त करेंगे।
यक्क - एडम नेवरामॉन्ट

1
आप की जरूरत नहीं है begin(X&&)। अस्थायी को मिडेयर auto&&में एक श्रेणी-आधारित द्वारा निलंबित कर दिया beginजाता है , और हमेशा एक अंतराल ( __range) के साथ कहा जाता है ।
टीसी

2
यह उत्तर वास्तव में एक टेम्पलेट उदाहरण से लाभान्वित होगा जो कोई भी कॉपी और कार्यान्वित कर सकता है।
टॉम ज़ातो -

मैं इट्रेटर प्रकार (*, ++; =!) के गुणों पर जोर देना चाहूंगा। मुझे आपसे यह पूछना चाहिए कि इस उत्तर को पुनरावृत्‍ति के लिए पुनरावृत्‍ति प्रकार के स्पेक्स बोल्‍ड करें।
रेड। वेव

62

मैं अपना जवाब लिखता हूं क्योंकि कुछ लोग एसटीएल के बिना सरल वास्तविक जीवन उदाहरण के साथ अधिक खुश हो सकते हैं।

मेरे पास किसी कारण से केवल अपना सादा डेटा सरणी कार्यान्वयन है, और मैं लूप के लिए रेंज का उपयोग करना चाहता था। यहाँ मेरा समाधान है:

 template <typename DataType>
 class PodArray {
 public:
   class iterator {
   public:
     iterator(DataType * ptr): ptr(ptr){}
     iterator operator++() { ++ptr; return *this; }
     bool operator!=(const iterator & other) const { return ptr != other.ptr; }
     const DataType& operator*() const { return *ptr; }
   private:
     DataType* ptr;
   };
 private:
   unsigned len;
   DataType *val;
 public:
   iterator begin() const { return iterator(val); }
   iterator end() const { return iterator(val + len); }

   // rest of the container definition not related to the question ...
 };

तब उपयोग उदाहरण:

PodArray<char> array;
// fill up array in some way
for(auto& c : array)
  printf("char: %c\n", c);

2
उदाहरण में प्रारंभ () और अंत () विधियाँ हैं, और इसमें एक बुनियादी (समझने में आसान) उदाहरण पुनरावृत्ति वर्ग भी है जिसे आसानी से किसी भी कस्टम कंटेनर प्रकार के लिए समायोजित किया जा सकता है। तुलना std :: array <> और किसी भी संभावित वैकल्पिक कार्यान्वयन का एक अलग सवाल है, और मेरी राय में लूप के लिए रेंज-बेस्ड से कोई लेना-देना नहीं है।
सीएसजेपेटर

यह एक बहुत ही संक्षिप्त और व्यावहारिक उत्तर है! मुझे ठीक इसी की तलाश थी! धन्यवाद!
Zac टेलर

1
क्या इसके लिए const रिटर्न क्वालीफायर निकालना अधिक उचित होगा const DataType& operator*(), और उपयोगकर्ता को उपयोग करने के लिए चुनना होगा const auto&या auto&? वैसे भी धन्यवाद, शानदार जवाब;)
रिक

53

मानक का प्रासंगिक हिस्सा 6.5.4 / 1 है:

यदि _RangeT एक वर्ग प्रकार है, तो unquali id ​​ed-id आरंभ और समाप्त होते हैं, वर्ग _RangeT के दायरे में देखा जाता है जैसे कि वर्ग सदस्य अभिगम लुकअप (3.4.5), और यदि या (या दोनों) ds कम से कम एक घोषणा को शुरू करते हैं, तो प्रारंभ करें - expr और एंड-expr हैं __range.begin()और __range.end(), क्रमशः;

- अन्यथा, शुरू-expr और अंत expr हैं begin(__range)और end(__range)क्रमश: कहाँ से शुरू और अंत तक तर्क पर निर्भर देखने (3.4.2) के साथ देखा जाता है। इस नाम लुकअप के प्रयोजनों के लिए, नामस्थान std एक संबद्ध नामस्थान है।

तो, आप निम्न में से कोई भी कर सकते हैं:

  • परिभाषित beginऔर endसदस्य कार्य
  • परिभाषित beginऔर endमुक्त कार्य जो ADL द्वारा मिलेंगे (सरलीकृत संस्करण: उन्हें कक्षा के समान नामस्थान में रखें)
  • विशेषज्ञ std::beginऔरstd::end

std::beginbegin()वैसे भी सदस्य फ़ंक्शन को कॉल करता है, इसलिए यदि आप केवल उपरोक्त में से किसी एक को लागू करते हैं, तो परिणाम वही होना चाहिए जो आप चुनते हैं। लूप्स के लिए राउंड-बेस्ड के लिए भी यही नतीजे हैं, और महज मॉर्टल कोड के लिए भी यही नतीजे हैं जिनके पास अपने जादुई नाम रिज़ॉल्यूशन के नियम नहीं हैं इसलिए बस using std::begin;एक अयोग्य कॉल का पालन करना पड़ता है begin(a)

यदि आप सदस्य फ़ंक्शन और ADL फ़ंक्शन को कार्यान्वित करते हैं , तो, सीमा-आधारित लूप के लिए सदस्य फ़ंक्शन को कॉल करना चाहिए, जबकि मात्र मॉर्टल्स ADL फ़ंक्शन को कॉल करेंगे। सुनिश्चित करें कि वे उस मामले में एक ही काम करते हैं!

आप लागू लिख रहे हैं बात तो कंटेनर इंटरफेस है, तो यह होगा begin()और end()पहले से ही सदस्य काम करता है, जो पर्याप्त होना चाहिए। यदि यह एक ऐसी श्रेणी है जो कंटेनर नहीं है (जो एक अच्छा विचार होगा यदि यह अपरिवर्तनीय है या यदि आपको आकार ऊपर नहीं पता है), तो आप चुनने के लिए स्वतंत्र हैं।

आपके द्वारा चुने गए विकल्पों में से, ध्यान दें कि आपको ओवरलोड नहीं करना चाहिएstd::begin() । आपको उपयोगकर्ता-परिभाषित प्रकार के लिए मानक टेम्पलेट्स को विशेषज्ञ बनाने की अनुमति है, लेकिन इससे अलग, नेमस्पेस std की परिभाषाओं को जोड़ना अपरिभाषित व्यवहार है। लेकिन वैसे भी, मानक कार्यों का विशेषज्ञता एक खराब विकल्प है यदि केवल इसलिए कि आंशिक फ़ंक्शन विशेषज्ञता की कमी का मतलब है कि आप इसे केवल एक वर्ग के लिए कर सकते हैं, वर्ग टेम्पलेट के लिए नहीं।


वहाँ कुछ आवश्यकताओं को पूरा नहीं कर रहे हैं कि itter बहुत मिलते हैं? यानी एक फारवर्डइटर हो या उन लाइनों के साथ कुछ।
पबबी

2
@ पबबी: 6.5.4 को देखते हुए, मुझे लगता है कि InputIterator पर्याप्त है। लेकिन असल में मुझे नहीं लगता कि प्रकार लौटाया करते है के लिए के लिए सीमा के आधार पर सभी को एक पुनरावर्तक किया जाना है। कथन मानक में परिभाषित किया गया है कि यह किसके समतुल्य है, इसलिए यह मानक में कोड में उपयोग किए जाने वाले केवल भावों को लागू करने के लिए पर्याप्त है: ऑपरेटर !=, उपसर्ग ++और अनुपयोगी *। यह लागू करने और सदस्य फ़ंक्शन या गैर-सदस्य ADL फ़ंक्शन को लागू करने के लिए शायद नासमझ है, जो एक पुनरावृत्ति के अलावा कुछ भी लौटाता है, लेकिन मुझे लगता है कि यह कानूनी है। विशेषता एक वापस जाने के लिए गैर इटरेटर यूबी है, मुझे लगता है। begin()end()std::begin
स्टीव जेसोप

क्या आप वाकई std :: start को ओवरलोड नहीं करना चाहते हैं? मैं पूछता हूं क्योंकि मानक पुस्तकालय कुछ मामलों में ही ऐसा करता है।
थ्रीबीट

@ थ्रीबिट: हाँ, मुझे यकीन है। मानक पुस्तकालय कार्यान्वयन के नियम कार्यक्रमों के नियमों से अलग हैं।
स्टीव जेसोप

3
इसे ओपन-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1442 के लिए अपडेट किया जाना चाहिए ।
टीसी

34

क्या मुझे अभी शुरुआत () और एंड () करनी चाहिए?

जहां तक ​​मुझे पता है, वह काफी है। आपको यह भी सुनिश्चित करना होगा कि सूचक को शुरू से अंत तक बढ़ाना होगा।

अगला उदाहरण (यह शुरू और अंत का गायब संस्करण है) ठीक संकलन करता है और काम करता है।

#include <iostream>
#include <algorithm>

int i=0;

struct A
{
    A()
    {
        std::generate(&v[0], &v[10], [&i](){  return ++i;} );
    }
    int * begin()
    {
        return &v[0];
    }
    int * end()
    {
        return &v[10];
    }

    int v[10];
};

int main()
{
    A a;
    for( auto it : a )
    {
        std::cout << it << std::endl;
    }
}

यहां कार्य के रूप में शुरुआत / अंत के साथ एक और उदाहरण दिया गया है। वे करने के लिए है ADL की वजह से, वर्ग के रूप में एक ही नाम स्थान में हो:

#include <iostream>
#include <algorithm>


namespace foo{
int i=0;

struct A
{
    A()
    {
        std::generate(&v[0], &v[10], [&i](){  return ++i;} );
    }

    int v[10];
};

int *begin( A &v )
{
    return &v.v[0];
}
int *end( A &v )
{
    return &v.v[10];
}
} // namespace foo

int main()
{
    foo::A a;
    for( auto it : a )
    {
        std::cout << it << std::endl;
    }
}

1
@ereOn उसी नामस्थान में जहां वर्ग परिभाषित है। दूसरा उदाहरण देखें
Bћовић

2
बधाई के रूप में :) यह दूसरे उदाहरण के लिए तर्क निर्भर डिपेंडेंट लुकअप (ADL) या Koenig लुकअप का उल्लेख करने के लायक हो सकता है (यह समझाने के लिए कि नि: शुल्क फ़ंक्शन समान नामस्थान में क्यों होना चाहिए क्योंकि यह जिस वर्ग से संचालित होता है)।
मैथ्यू एम। एम।

1
@ereOn: वास्तव में, आप नहीं। ADL, लुक-अप को स्वचालित रूप से उन नामस्थानों को शामिल करने के बारे में है जो तर्कों से संबंधित हैं। अधिभार संकल्प के बारे में एक अच्छा एसीसीयू लेख है , जो दुर्भाग्य से नाम लुकअप भाग को छोड़ देता है। नाम लुकअप में उम्मीदवारों के फ़ंक्शन को इकट्ठा करना शामिल है, आप वर्तमान स्कोप + तर्कों के स्कोप को देखकर शुरू करते हैं। यदि उस मैच का कोई नाम नहीं मिलता है, तो आप वर्तमान दायरे के मूल दायरे में जाते हैं और फिर से खोज करते हैं ... जब तक आप वैश्विक दायरे में नहीं पहुंच जाते।
Matthieu M.

1
@ B inовиЈ क्षमा करें, लेकिन अंत में किस कारण से () फ़ंक्शन आप एक खतरनाक सूचक लौटाते हैं? मुझे पता है कि यह काम करता है, लेकिन मैं इसके तर्क को समझना चाहता हूं। सरणी का अंत v [9] है, आप कभी v [10] क्यों लौटाएंगे?
गदामियल

1
@ बीजामृत मैं सहमत हूँ। मुझे लगता है कि यह होना चाहिए return v + 10&v[10]सरणी के ठीक पहले मेमोरी लोकेशन को डीरेफर करते हैं।
मिल्ली स्मिथ

16

मामले में आप अपने साथ सीधे एक वर्ग की यात्रा बैकअप लेना चाहते हैं std::vectorया std::mapसदस्य, यहाँ उस के लिए कोड है:

#include <iostream>
using std::cout;
using std::endl;
#include <string>
using std::string;
#include <vector>
using std::vector;
#include <map>
using std::map;


/////////////////////////////////////////////////////
/// classes
/////////////////////////////////////////////////////

class VectorValues {
private:
    vector<int> v = vector<int>(10);

public:
    vector<int>::iterator begin(){
        return v.begin();
    }
    vector<int>::iterator end(){
        return v.end();
    }
    vector<int>::const_iterator begin() const {
        return v.begin();
    }
    vector<int>::const_iterator end() const {
        return v.end();
    }
};

class MapValues {
private:
    map<string,int> v;

public:
    map<string,int>::iterator begin(){
        return v.begin();
    }
    map<string,int>::iterator end(){
        return v.end();
    }
    map<string,int>::const_iterator begin() const {
        return v.begin();
    }
    map<string,int>::const_iterator end() const {
        return v.end();
    }

    const int& operator[](string key) const {
        return v.at(key);
    }
    int& operator[](string key) {
        return v[key];
    } 
};


/////////////////////////////////////////////////////
/// main
/////////////////////////////////////////////////////

int main() {
    // VectorValues
    VectorValues items;
    int i = 0;
    for(int& item : items) {
        item = i;
        i++;
    }
    for(int& item : items)
        cout << item << " ";
    cout << endl << endl;

    // MapValues
    MapValues m;
    m["a"] = 1;
    m["b"] = 2;
    m["c"] = 3;
    for(auto pair: m)
        cout << pair.first << " " << pair.second << endl;
}

2
यह है कि उल्लेख के लायक const_iteratorभी एक में पहुँचा जा सकता है autoके माध्यम से (सी ++ 11) संगत तरीका cbegin, cendआदि
underscore_d

2

यहाँ, मैं कस्टम प्रकार बनाने के सरलतम उदाहरण साझा कर रहा हूँ, उस के साथ "काम करेंगे पाश के लिए सीमा के आधार पर ":

#include<iostream>
using namespace std;

template<typename T, int sizeOfArray>
class MyCustomType
{
private:
    T *data;
    int indx;
public:
    MyCustomType(){
        data = new T[sizeOfArray];
        indx = -1;
    }
    ~MyCustomType(){
        delete []data;
    }
    void addData(T newVal){
        data[++indx] = newVal;
    }

    //write definition for begin() and end()
    //these two method will be used for "ranged based loop idiom"
    T* begin(){
        return &data[0];
    }
    T* end(){
        return  &data[sizeOfArray];
    }
};
int main()
{
    MyCustomType<double, 2> numberList;
    numberList.addData(20.25);
    numberList.addData(50.12);
    for(auto val: numberList){
        cout<<val<<endl;
    }
    return 0;
}

आशा है, यह मेरे जैसे कुछ नौसिखिया डेवलपर के लिए उपयोगी होगा: पी :)
धन्यवाद।


अपने अंतिम पद्धति में अमान्य मेमोरी से बचने के लिए एक अतिरिक्त तत्व क्यों नहीं आवंटित किया जाए?
एंडर्सक

@ एंडर्स क्योंकि लगभग सभी अंत-पुनरावृत्तियां उनके युक्त संरचना के अंत के बाद इंगित करती हैं। end()समारोह में ही स्पष्ट रूप से, क्योंकि यह केवल इस स्मृति स्थान 'के को पता' लेता है, एक अनुचित स्मृति स्थान भिन्नता नहीं है। एक अतिरिक्त तत्व जोड़ने का मतलब होगा कि आपको अधिक मेमोरी की आवश्यकता होगी, और your_iterator::end()किसी भी तरह से उपयोग करने से उस मूल्य को किसी भी अन्य पुनरावृत्तियों के साथ काम नहीं करेगा क्योंकि वे उसी तरह से बनाए गए हैं।
Qqwy

@Qqwy अपनी अंतिम विधि de-refences - return &data[sizeofarray]IMHO इसे केवल पता डेटा + sizeofarray वापस करना चाहिए लेकिन मुझे क्या पता है,
AndersK

@ और आप सही हैं। मुझे तेज रखने के लिए धन्यवाद :-)। हां, data + sizeofarrayइसे लिखने का सही तरीका होगा।
Qqwy

1

क्रिस रेडफोर्ड का जवाब Qt कंटेनरों के लिए भी काम करता है (बेशक)। यहाँ एक अनुकूलन है (नोटिस मैं एक constBegin()क्रमशः constEnd(), const_iterator विधियों से लौटता हूं):

class MyCustomClass{
    QList<MyCustomDatatype> data_;
public:    
    // ctors,dtor, methods here...

    QList<MyCustomDatatype>::iterator begin() { return data_.begin(); }
    QList<MyCustomDatatype>::iterator end() { return data_.end(); }
    QList<MyCustomDatatype>::const_iterator begin() const{ return data_.constBegin(); }
    QList<MyCustomDatatype>::const_iterator end() const{ return data_.constEnd(); }
};

0

मैं @ जेस्वे के उत्तर के कुछ हिस्सों को विस्तार से बताना चाहूंगा, जिसके लिए पहले मुझे समझ नहीं आया था। आशा करता हूँ की ये काम करेगा।

std::beginbegin()वैसे भी सदस्य फ़ंक्शन को कॉल करता है, इसलिए यदि आप केवल उपरोक्त में से किसी एक को लागू करते हैं, तो परिणाम वही होना चाहिए जो आप चुनते हैं। कि लूप के लिए राउंड-बेस्ड के लिए भी यही परिणाम है, और केवल नश्वर कोड के लिए भी वही परिणाम है, जिसमें अपने जादुई नाम रिज़ॉल्यूशन नियम नहीं हैं, इसलिए बस using std::begin;एक अयोग्य कॉल का पालन करना है begin(a)

यदि आप सदस्य कार्यों और ADL कार्यों को कार्यान्वित करते हैं , हालांकि, तो लूप के लिए रेंज-आधारित को सदस्य फ़ंक्शन को कॉल करना चाहिए, जबकि मात्र मॉर्टल्स ADL फ़ंक्शन को कॉल करेंगे। सुनिश्चित करें कि वे उस मामले में एक ही काम करते हैं!


https://en.cppreference.com/w/cpp/language/range-for :

  • अगर ...
  • यदि range_expressionएक वर्ग प्रकार की अभिव्यक्ति है जिसमें Cनाम वाला एक सदस्य beginऔर एक सदस्य नाम है end(भले ही इस तरह के सदस्य की पहुंच या पहुंच), तो begin_exprहै __range.begin() और end_exprहै __range.end();
  • अन्यथा, begin_exprहै begin(__range)और end_exprहै end(__range), जो तर्क-निर्भर लुकअप के माध्यम से पाया जाता है (गैर-एडीएल लुकअप नहीं किया जाता है)।

लूप के लिए रेंज-आधारित के लिए, सदस्य कार्यों को पहले चुना जाता है।

लेकिन के लिए

using std::begin;
begin(instance);

ADL फ़ंक्शन पहले चुने गए हैं।


उदाहरण:

#include <iostream>
#include <string>
using std::cout;
using std::endl;

namespace Foo{
    struct A{
        //member function version
        int* begin(){
            cout << "111";
            int* p = new int(3);  //leak I know, for simplicity
            return p;
        }
        int *end(){
            cout << "111";
            int* p = new int(4);
            return p;
        }
    };

    //ADL version

    int* begin(A a){
        cout << "222";
        int* p = new int(5);
        return p;
    }

    int* end(A a){
        cout << "222";
        int* p = new int(6);
        return p;
    }

}

int main(int argc, char *args[]){
//    Uncomment only one of two code sections below for each trial

//    Foo::A a;
//    using std::begin;
//    begin(a);  //ADL version are selected. If comment out ADL version, then member functions are called.


//      Foo::A a;
//      for(auto s: a){  //member functions are selected. If comment out member functions, then ADL are called.
//      }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.