[c++] सी ++ 11 रिवर्स रेंज-आधारित-लूप


Answers

असल में, सी ++ 14 में यह कोड की बहुत कम लाइनों के साथ किया जा सकता है।

यह @ पॉल के समाधान के विचार में बहुत समान है। सी ++ 11 से गुम चीजों के कारण, यह समाधान थोड़ा अनावश्यक रूप से फूला हुआ है (प्लस स्टेड गंध में परिभाषित)। सी ++ 14 के लिए धन्यवाद हम इसे और अधिक पठनीय बना सकते हैं।

मुख्य अवलोकन यह है कि सीमा-आधारित-लूप्स रेंज के इटरेटर प्राप्त करने के लिए begin() और end() पर भरोसा करते हुए काम करते हैं। ADL के लिए धन्यवाद, किसी को std :: नेमस्पेस में अपने कस्टम begin() और end() को परिभाषित करने की भी आवश्यकता नहीं है।

यहां एक बहुत ही सरल-नमूना समाधान है:

// -------------------------------------------------------------------
// --- Reversed iterable
using std::rbegin,
      std::rend;

template <typename T>
struct reversion_wrapper { T& iterable; };

template <typename T>
auto begin (reversion_wrapper<T> w) { return rbegin(w.iterable); }

template <typename T>
auto end (reversion_wrapper<T> w) { return rend(w.iterable); }

template <typename T>
reversion_wrapper<T> reverse (T&& iterable) { return { iterable }; }

यह एक आकर्षण की तरह काम करता है, उदाहरण के लिए:

template <typename T>
void print_iterable (ostream& out, const T& iterable)
{
    for (auto&& element: iterable)
        out << element << ',';
    cout << '\n';
}

int main (int, char**)
{
    // on prvalues
    print_iterable(cout, reverse(initializer_list<int> { 1, 2, 3, 4, }));

    // on const lvalue references
    const list<int> ints_list { 1, 2, 3, 4, };
    for (auto&& el: reverse(ints_list))
        cout << el << ',';
    cout << '\n';

    // on mutable lvalue references
    vector<int> ints_vec { 0, 0, 0, 0, };
    size_t i = 0;
    for (int& el: reverse(ints_vec))
        el += i++;
    print_iterable(cout, ints_vec) << '\n';
    print_iterable(cout, reverse(ints_vec)) << '\n';


    return 0;
}

उम्मीद के रूप में प्रिंट

4,3,2,1,
4,3,2,1,
3,2,1,0,
0,1,2,3,

नोट std::rbegin() , std::rend() , और std::make_reverse_iterator() अभी तक जीसीसी std::make_reverse_iterator() में लागू नहीं किए गए हैं। मैं इन उदाहरणों को मानक के अनुसार लिखता हूं, लेकिन वे स्थिर जी ++ में संकलित नहीं होंगे। फिर भी, इन तीन कार्यों के लिए अस्थायी स्टब्स जोड़ना बहुत आसान है। यहां एक नमूना कार्यान्वयन है, निश्चित रूप से पूरा नहीं हुआ है लेकिन ज्यादातर मामलों के लिए पर्याप्त रूप से काम करता है:

// --------------------------------------------------
template <typename I>
reverse_iterator<I> make_reverse_iterator (I i)
{
    return std::reverse_iterator<I> { i };
}

// --------------------------------------------------
template <typename T>
auto rbegin (T& iterable)
{
    return make_reverse_iterator(iterable.end());
}

template <typename T>
auto rend (T& iterable)
{
    return make_reverse_iterator(iterable.begin());
}

// const container variants

template <typename T>
auto rbegin (const T& iterable)
{
    return make_reverse_iterator(iterable.end());
}

template <typename T>
auto rend (const T& iterable)
{
    return make_reverse_iterator(iterable.begin());
}

अद्यतन 22 अक्टूबर 2017

इस बात को इंगित करने के लिए estan लिए धन्यवाद।

मूल उत्तर नमूना कार्यान्वयन using namespace std; उपयोग using namespace std; करता है using namespace std; , जो इस कार्यान्वयन सहित किसी भी फ़ाइल का कारण बनता है (जिसे हेडर फ़ाइल में होना चाहिए), पूरे std नेमस्पेस को आयात करने के लिए भी।

using std::rbegin, std::rend बजाय प्रस्तावित करने के लिए नमूना कार्यान्वयन में using std::rbegin, std::rend किया गया।

Question

क्या कोई कंटेनर एडेप्टर है जो इटरेटर की दिशा को उलट देगा ताकि मैं रेंज-आधारित फॉर-लूप के साथ एक कंटेनर पर फिर से चालू हो सकूं?

स्पष्ट पुनरावृत्तियों के साथ मैं इसे परिवर्तित कर दूंगा:

for (auto i = c.begin(); i != c.end(); ++i) { ...

इस मामले में:

for (auto i = c.rbegin(); i != c.rend(); ++i) { ...

मैं इसे बदलना चाहता हूं:

for (auto& i: c) { ...

इसके लिए:

for (auto& i: std::magic_reverse_adapter(c)) { ...

क्या ऐसी कोई चीज है या क्या मुझे इसे खुद लिखना है?




क्या यह आपके लिए काम करता है:

#include <iostream>
#include <list>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator_range.hpp>

int main(int argc, char* argv[]){

  typedef std::list<int> Nums;
  typedef Nums::iterator NumIt;
  typedef boost::range_reverse_iterator<Nums>::type RevNumIt;
  typedef boost::iterator_range<NumIt> irange_1;
  typedef boost::iterator_range<RevNumIt> irange_2;

  Nums n = {1, 2, 3, 4, 5, 6, 7, 8};
  irange_1 r1 = boost::make_iterator_range( boost::begin(n), boost::end(n) );
  irange_2 r2 = boost::make_iterator_range( boost::end(n), boost::begin(n) );


  // prints: 1 2 3 4 5 6 7 8 
  for(auto e : r1)
    std::cout << e << ' ';

  std::cout << std::endl;

  // prints: 8 7 6 5 4 3 2 1
  for(auto e : r2)
    std::cout << e << ' ';

  std::cout << std::endl;

  return 0;
}



यदि सी ++ 14 का उपयोग नहीं करते हैं, तो मैं सबसे सरल समाधान से नीचे मिलता हूं।

#define METHOD(NAME, ...) auto NAME __VA_ARGS__ -> decltype(m_T.r##NAME) { return m_T.r##NAME; }
template<typename T>
struct Reverse
{
  T& m_T;

  METHOD(begin());
  METHOD(end());
  METHOD(begin(), const);
  METHOD(end(), const);
};
#undef METHOD

template<typename T>
Reverse<T> MakeReverse (T& t) { return Reverse<T>{t}; }

Demo
यह कंटेनरों / डेटा-प्रकारों (जैसे सरणी) के लिए काम नहीं करता है, जो begin/rbegin, end/rend कार्यों नहीं है।






Links



Tags

c++ c++   c++11