c ++ stl-совместимый итератор итераторов - PullRequest
6 голосов
/ 01 июля 2011

Что я пытаюсь сделать

У меня есть метод, который разделяет вещи. Этот метод не сортирует массив полностью; он просто разделяет массив так, что все элементы на одной стороне (некоторого заранее определенного «центра» или «значения средней точки» - хотя это не должно приводить к равномерному разбиению) меньше «центра» и все элементы на другой стороне больше, чем центр. Точка: это не «сортировка» в традиционном смысле; это раздел.

Когда я делю вещи, мне нужно держать ключ; так что, когда все поменяно местами, ключ поменялся местами; и если в какой-то момент в будущем я захочу отменить раздел, я могу изменить порядок вещей на основе ключа.

Очевидно, чтобы переставить вещи на основе значения ключа, я мог бы сделать что-то вроде

std::vector< std::pair< std::size_t , my::thingie > > vp;
std::vector< std::size_t >::iterator itKey( key.begin() );
// itThingie_begin and itThingie_end exist; I don't have direct access to the container 
my::thingie::iterator itThingie( itThingie_begin );
for (; itKey != key.end(); ++itKey; ++itThingie ) vp.push_back( *itKey, *itThingie );
std::sort( vp.begin() , vp.end() , &comp_pair_first );
itThingie = itThingie_begin;
for ( std::vector< std::pair< std::size_t , my::thingie > >::const_iterator p=vp.begin(); p!=vp.end(); ++p, ++itThingie ) *itThingie = p->second;

То есть я мог бы скопировать все ключ и данные в пару; и отсортировать пару по ее первому значению (ключу), используя пользовательский предикат сравнения (или с boost :: bind); а затем скопируйте все данные обратно. Я это понимаю. Это не идеально, потому что у меня, вероятно, есть пара сотен мегабайт штук, и вышеупомянутый подход включает в себя копирование этого во временный, сортировку временного и последующее копирование обратно.

Это также не идеально, потому что мой метод разбиения, как он существует в настоящее время, нуждается в начальных и конечных итераторах ключа И вещи (потому что он должен менять оба при каждом обмене). Более того, - и вот кикер - я должен переписать свой метод разбиения, если есть два набора штук; У меня есть ключ, вещь, которая определяет сторону раздела, и еще одна багажная вещь, у которой есть другая информация, которую я хочу использовать для других алгоритмов.

Теперь, разумеется, я не хочу переписывать метод разделения каждый раз Я хочу включить какой-то другой итератор для обмена "в тандоме" с разделом. Так что, как и раньше, я мог бы скопировать все эти вещи во временную std :: pair (или вложенные пары, если мне нужно поменять местами несколько вещей в тандеме), а затем разбить ее на части, посмотрев сначала на std :: pair ::, а затем скопировать временные данные обратно ... Но это чрезвычайно расточительно, потому что каждая дополнительная «багажная» вещь, которую я добавляю, также, вероятно, сотни мегабайт.

Я знаю, что мог бы сделать это таким образом. Я не хочу делать это таким образом, потому что это слишком много памяти.

Как я хочу это сделать

Проблема, которую я описал выше, заключается просто в работе с итераторами в тандеме. Поэтому я хотел бы иметь коллекцию итераторов, которая абстрагирует содержимое этой коллекции.

Я хочу иметь коллекцию итераторов. Я называю эту коллекцию питером (это пара итераторов). Когда кто-то меняет два питера, один действительно выполняет std :: iter_swap на своих первых итераторах (а также на своих вторых итераторах).

Я хочу иметь итератор Питера (называемый Питератором), который имеет все характеристики итератора, но когда он увеличивается и уменьшается, он увеличивает и уменьшает первый и второй итераторы Питера. Когда piterator разыменовывается, он должен возвращать ссылку на piter, который является коллекцией итераторов. Все расстояния могут быть измерены по первому компоненту питера. Или, в общем, если есть какой-либо вопрос, на который необходимо ответить, и неясно, какой итератор должен на него ответить, тогда на него должен ответить первый итератор питера.

Если я хочу создать питератор, который может использовать итератор в большем числе итераторов, я могу создать питератор, чей питер содержит итератор (первый) и еще один питератор (второй).

Итак, вот что я попробовал (я также пытался использовать boost :: iterator_facade, но у меня возникла та же проблема - как описано ниже.)

#include <vector>
#include <iostream>
#include <algorithm>
#include <utility>
#include <iterator>


//
// pair of iterators
//
template <typename T,typename U>
struct piter : public std::pair<T,U>
{
  piter() : std::pair<T,U>() {};
  piter( T const & l , U const & r ) : std::pair<T,U>(l,r) {};
  piter( std::pair<T,U> const & p ) { this->first = p.first; this->second = p.second;     };
   //piter( std::pair<T,U> const p ) { this->first = p.first; this->second = p.second;      };

  template <typename OT, typename OU>
  piter( piter<OT,OU> const & p ) : std::pair<T,U>::first(p.first), std::pair<T,U>::second(p.second) {}

  piter<T,U> & operator=( piter<T,U> const & rhs )
  {
    if( &rhs != this ) { *this->first  = *rhs.first; *this->second = *rhs.second; }
    return *this;
  };


  friend void swap( piter<T,U> & lhs, piter<T,U> & rhs )
  {
    using std::swap;

    std::cout << "piter::swap; WAS: " << *lhs.first << " <-> " << *rhs.first << std::endl;

    std::iter_swap(lhs.first,rhs.first);
    std::iter_swap(lhs.second,rhs.second);

    std::cout << "piter::swap; NOW: " << *lhs.first << " <-> " << *rhs.first << std::endl;
  };

};




//
// iterator of pair of iterators
//
template <typename T, typename U>
class piterator : public std::iterator< std::random_access_iterator_tag,
                        piter<T,U>, 
                        std::ptrdiff_t,
                        piter<T,U> *,
                        piter<T,U> & >
{
  typedef piterator<T,U> iter;

public: // Traits typedefs, which make this class usable with algorithms which need a random access iterator.
  typedef std::random_access_iterator_tag iterator_category;
  typedef piter<T,U>     value_type;
  typedef std::ptrdiff_t difference_type;
  typedef piter<T,U> *   pointer;
  typedef piter<T,U> &   reference;


public:

  piterator() {};
  piterator( iter const &    rhs ) { this->mp.first = rhs.mp.first;  this->mp.second = rhs.mp.second;};
  piterator( pointer         rhs ) { this->mp.first = rhs->first;    this->mp.second = rhs->second; };
  //piterator( reference const rhs ) { this->mp.first = rhs.first;     this->mp.second = rhs.second; };
  piterator( value_type const rhs ) { this->mp.first = rhs.first;     this->mp.second = rhs.second; };


  iter & operator=( iter const & rhs )
  {
    if ( &rhs != this ){ this->mp.first = rhs.mp.first; this->mp.second = rhs.mp.second; };
    return *this;
  }

  friend void swap( iter & lhs , iter & rhs )
  {
    using std::swap;

    std::cout << "piterator::swap; WAS:  lhs " << *lhs->first << " rhs " << *rhs->first << std::endl;

    swap(lhs.mp,rhs.mp);

    std::cout << "piterator::swap; NOW:  lhs " << *lhs->first << " rhs " << *rhs->first << std::endl;
  }



public: // Comparison
  // Note: it's an error to compare iterators over different files.
  bool operator< ( iter const & rhs ) const { return mp.first < rhs.mp.first; }
  bool operator> ( iter const & rhs ) const { return mp.first > rhs.mp.first; }
  bool operator==( iter const & rhs ) const { return mp.first == rhs.mp.first; }
  bool operator!=( iter const & rhs ) const { return mp.first != rhs.mp.first; }

public: // Iteration
  iter & operator++()  { ++mp.first; ++mp.second; return *this; }
  iter & operator--()  { --mp.first; --mp.second; return *this; }
  iter operator++(int) { iter tmp(*this); ++(*this); return tmp; }
  iter operator--(int) { iter tmp(*this); --(*this); return tmp; }

public: // Step
  iter & operator+=( difference_type n ) { mp.first += n; mp.second += n; return *this; }
  iter & operator-=( difference_type n ) { mp.first -= n; mp.second -= n; return *this; }
  iter operator+   ( difference_type n ) { iter result(*this); return result += n; }
  iter operator-   ( difference_type n ) { iter result(*this); return result -= n; }

public: // Distance
  difference_type operator-( iter & rhs ) { return mp.first - rhs.mp.first; }

public: // Access
  reference operator*() { return mp; }
  reference operator[]( difference_type n ) { return *(*this+n); }
  pointer operator->() { return &mp; };

private: // State

  value_type mp;

};








template<class T,class U>
bool proxy_comp( piter<T,U> left, piter<T,U> right )
{ 
  std::cout << "proxy_comp: " << *(left.first) << " > " << *(right.first) << " ?=? " <<  ( *(left.first) > *(right.first) ) << std::endl;  
  return *left.first > *right.first; 
}




int main()
{

  std::vector<double> dv(3);
  std::vector<int> iv(3);
  dv[0]  = -0.5; dv[1]  = -1.5; dv[2]  = -2.5;
  iv[0]  =  10;  iv[1]  = 20;   iv[2]  =  3;


  typedef piterator< std::vector<int>::iterator , std::vector<double>::iterator > PAIR_ITER;
  typedef PAIR_ITER::value_type PAIR_REF;

  PAIR_ITER pair_begin( PAIR_REF( iv.begin() , dv.begin() ) );
  PAIR_ITER pair_end(   PAIR_REF( iv.end()   , dv.end()   ) );


  std::cout << "paired arrays now:" << std::endl;
  for ( PAIR_ITER p = pair_begin; p != pair_end; ++p )
    std::cout << *p->first << " " << *p->second << std::endl;

  std::cout << "swap 1st and 3rd elements..." << std::endl;
  swap(*pair_begin,*(pair_begin+2));

  std::cout << "paired arrays now:" << std::endl;
  for ( PAIR_ITER p = pair_begin; p != pair_end; ++p )
    std::cout << *p->first << " " << *p->second << std::endl;

  std::cout << "calling sort..." << std::endl;
  std::sort( pair_begin , pair_end , &proxy_comp<std::vector<int>::iterator , std::vector<double>::iterator> );

  std::cout << "paired arrays now:" << std::endl;
  for ( PAIR_ITER p = pair_begin; p != pair_end; ++p )
    std::cout << *p->first << " " << *p->second << std::endl;


  return 0;
}

ПРОБЛЕМА Питер и питератор, кажется, работают, когда я пытаюсь использовать его так же, как я использую все другие итераторы, но он не работает корректно с алгоритмами STL.

Приведенный выше код показывает, что питер правильно поменяется, но он не сортируется правильно.

Вывод вышеуказанного кода:

    paired arrays now:
    10 -0.5
    20 -1.5
    3 -2.5
    swap 1st and 3rd elements...
    piter::swap; WAS: 10 <-> 3
    piter::swap; NOW: 3 <-> 10
    paired arrays now:
    3 -2.5
    20 -1.5
    10 -0.5
    calling sort...
    proxy_comp: 20 > 3 ?=? 1
    proxy_comp: 10 > 3 ?=? 1
    paired arrays now:
    3 -2.5
    3 -2.5
    3 -2.5

ВОПРОС:

Что мне нужно изменить, чтобы std :: sort (или, в идеале, stl в целом) корректно работал с piterators?

Ответы [ 2 ]

5 голосов
/ 03 июля 2011

OK.Проблема связана с тем, как stl перемещает память.Если бы он использовал swap () все время, тогда все было бы хорошо, но иногда это так (из glu's stl_algo.h __insertion_sort)

if (__comp(*__i, *__first))
{
  // COPY VALUE INTO TEMPORARY MEMORY 
    typename iterator_traits<_RandomAccessIterator>::value_type __val = _GLIBCXX_MOVE(*__i);
  // MOVE MEMORY AROUND
   _GLIBCXX_MOVE_BACKWARD3(__first, __i, __i + 1);
  // COPY TEMPORARY VALUE BACK
    *__first = _GLIBCXX_MOVE(__val);
}

Итак, мы видим, что итератор :: value_type должен хранитьЗНАЧЕНИЕ.Сам по себе он не может быть указателем.Если это был указатель, то он полностью делает недействительным подход псевдоперемены, показанный выше.

Поэтому нам нужно создать вспомогательный класс, который является коллекцией VALUES, а не коллекцией ITERATORS.Мы можем вернуть этот вспомогательный класс, когда оператор разыменования piterator является постоянным, например,

value_type operator*() const { return helper_class_value_collection_ctor( _args_ ); };

Таким образом, мы можем хранить значения в новой памяти.

Кроме того, нам нужносоздайте другой вспомогательный класс, который является коллекцией ITERATORS, в отличие от VALUES, так что выражения типа

*piterator_a = *piterator_b

являются допустимыми.Если * piterator_a возвращает значение-значение, то установка этих значений не имеет смысла, поскольку возвращаемое значение является временным.Таким образом, в этом случае нам нужен оператор разыменования, чтобы возвратить ссылочный тип (набор итераторов), который будет храниться как частная переменная-член piterator.

reference operator*() { return private_reftype_variable; };

Таким образом, все это приводит к выводамк следующему.

#include <vector>
#include <iostream>
#include <utility>
#include <iterator>
#include <algorithm>


// forward decl
template <typename T,typename U> struct piterator_iterators;

template <typename T,typename U>
struct piterator_values
{
  // This class holds memory; it is a value_type
  // It only serves the purpose of 
  // allowing the stl to hold temporary values when moving memory around.
  // If the stl called sort(), then this class wouldn't be necessary.
  //
  // Note that the memory may be set by a piterator_iterators class,
  // which is a pseudo-value_type that points at memory, instead of holding memory.
  //
  // YOU NEED THIS SO THAT
  // typename piterator<T,U>::value_type Tmp = *piterator_a
  // PLACES THE VALUES INTO SOME (ACTUAL) TEMPORARY MEMORY, AS OPPOSED
  // TO CREATING A NEW POINTER TO EXISTING MEMORY.

  typedef typename T::value_type first_value;
  typedef typename U::value_type second_value;

  first_value  first;
  second_value second;

  piterator_values() {};
  piterator_values( first_value const & first , second_value const & second ) : first(first), second(second) {};
  piterator_values( piterator_values<T,U> const & rhs ) : first(rhs.first), second(rhs.second) { };
  piterator_values( piterator_iterators<T,U> const & rhs ) : first(*rhs.first), second(*rhs.second) { };


  piterator_values<T,U> & operator=( piterator_values<T,U> const & rhs )
  {
    if( &rhs != this ) 
      {
    first  = rhs.first; 
    second = rhs.second; 
      }
    return *this;
  };

  piterator_values<T,U> & operator=( piterator_iterators<T,U> const & rhs )
  {
    if( &rhs != this ) 
      {
    first  = *rhs.first; 
    second = *rhs.second; 
      }
    return *this;
  };


  friend void swap( piterator_values<T,U> & lhs, piterator_values<T,U> & rhs )
  {
    using std::swap;
    swap(lhs.first,rhs.first);
    swap(lhs.second,rhs.second);
  };


};





template <typename T,typename U>
struct piterator_iterators
{

  T first;
  U second;

  // This class does not hold memory; it points at existing memory.
  // It is a pseudo-value_type.  When the piterator dereferences, it
  // will return a piterator_iterators object IF it is a nonconst reference.
  // This class is used as a "reference" for an actual iterator,
  // so assignment operators change the value of the thing pointed at,
  // as opposed to reseting the address of what is being pointed at.
  //
  // YOU NEED THIS SO THAT
  // *piterator_a = *piterator_b
  // MAKES SENSE.
  // IF THE DEREFERENCE PASSED A piterator_values, 
  // THEN IT WOULD ONLY MODIFY A TEMPORARY, NOT THE ACTUAL THING
  //
  piterator_iterators() {};
  piterator_iterators( T const & first , U const & second ) : first(first), second(second) {};
  piterator_iterators( piterator_iterators<T,U> const & rhs ) : first(rhs.first), second(rhs.second) {};


  piterator_iterators<T,U> & operator=( piterator_iterators<T,U> const & rhs )
  {
    if( &rhs != this ) 
      {
    *first  = *rhs.first; 
    *second = *rhs.second; 
      }
    return *this;
  };

  piterator_iterators<T,U> & operator=( piterator_values<T,U> const & rhs )
  {
    *first  = rhs.first; 
    *second = rhs.second; 
    return *this;
  };


  friend void swap( piterator_iterators<T,U> & lhs, piterator_iterators<T,U> & rhs )
  {
    using std::swap;
    std::iter_swap(lhs.first,rhs.first);
    std::iter_swap(lhs.second,rhs.second);
  };


};




//
// iterator of pair of iterators
//
template <typename T, typename U>
class piterator : public std::iterator< std::random_access_iterator_tag, piterator_values<T,U>, std::ptrdiff_t, piterator_iterators<T,U> *, piterator_iterators<T,U> & >
{
  typedef piterator<T,U> iter;

public: 

  typedef std::random_access_iterator_tag iterator_catagory;
  typedef typename piterator<T,U>::value_type      value_type;
  typedef typename piterator<T,U>::difference_type difference_type;
  typedef typename piterator<T,U>::pointer         pointer;
  typedef typename piterator<T,U>::reference       reference;
  typedef piterator_iterators<T,U>                 value_of_reference;
  //typedef typename piterator_iterators<T,U> & reference;

public:

  piterator() {};
  piterator( iter const & rhs ) { mp.first = rhs.mp.first;  mp.second = rhs.mp.second; };
  piterator( value_of_reference const rhs ) { mp.first = rhs.first; mp.second = rhs.second; };
  piterator( T const first, U const second ) { mp.first = first; mp.second = second; };


  iter & operator=( iter const & rhs )
  {
    if ( &rhs != this )
      {
    mp.first = rhs.mp.first; 
    mp.second = rhs.mp.second; 
      };
    return *this;
  }

  friend void swap( iter & lhs , iter & rhs )
  {
    using std::swap;
    swap(lhs.mp,rhs.mp);
  }



public: // Comparison
  bool operator< ( iter const & rhs ) const { return mp.first < rhs.mp.first; }
  bool operator> ( iter const & rhs ) const { return mp.first > rhs.mp.first; }
  bool operator==( iter const & rhs ) const { return mp.first == rhs.mp.first; }
  bool operator!=( iter const & rhs ) const { return mp.first != rhs.mp.first; }

public: // Iteration
  iter & operator++()  { ++(mp.first); ++(mp.second); return *this; }
  iter & operator--()  { --(mp.first); --(mp.second); return *this; }
  iter operator++(int) { iter tmp(*this); ++(*this); return tmp; }
  iter operator--(int) { iter tmp(*this); --(*this); return tmp; }

public: // Step
  iter & operator+=( difference_type n ) { mp.first += n; mp.second += n; return *this; }
  iter & operator-=( difference_type n ) { mp.first -= n; mp.second -= n; return *this; }
  iter operator+   ( difference_type n ) { iter result(*this); return result += n; }
  iter operator-   ( difference_type n ) { iter result(*this); return result -= n; }
  difference_type operator+   ( iter const & rhs ) { return mp.first + rhs.mp.first; }
  difference_type operator-   ( iter const & rhs ) { return mp.first - rhs.mp.first; }

public: // Distance
  difference_type operator-( iter & rhs ) { return mp.first - rhs.mp.first; }

public: // Access
  // reference if on the lhs of the eq.
  reference operator*() { return mp; }
  // value if on the rhs of the eq.
  value_type operator*() const { return value_type(*mp.first,*mp.second); }

  reference operator[]( difference_type n ) { return *( (*this) + n ); }
  pointer operator->() { return &mp; };

private: // State

  value_of_reference mp;

};

Вот основная программа, отделенная сверху, чтобы увидеть, как используется вышеупомянутое ....

////////////////////////////////////////////////////////////////
template<class T,class U>
bool proxy_comp( piterator_values<T,U> left, piterator_values<T,U> right )
{ 
  return left.first < right.first; 
}
///////////////////////////////////////////////////////////////
int main()
{

  std::vector<double> dv1(3);
  std::vector<double> dv2(3);
  std::vector<int> iv(3);
  dv1[0]  = -0.5; dv1[1]  = -1.5; dv1[2]  = -2.5;
  dv2[0]  = 10.5; dv2[1]  = 11.5; dv2[2]  = 12.5;
  iv[0]   = 10;    iv[1]  = 20;   iv[2]   =  3;

  //
  // EXAMPLE 1: PAIR OF ITERATORS
  //

  typedef piterator< std::vector<int>::iterator , std::vector<double>::iterator > PAIR_ITER;

  PAIR_ITER pair_begin( iv.begin() , dv1.begin() );
  PAIR_ITER pair_end( iv.end() , dv1.end() );

  std::cout << "paired arrays now:" << std::endl;
  for ( PAIR_ITER p = pair_begin; p != pair_end; ++p )
    std::cout << *p->first << " " << *p->second << std::endl;

  std::cout << "swap 1st and 3rd elements..." << std::endl;
  swap(*pair_begin,*(pair_begin+2));

  std::cout << "paired arrays now:" << std::endl;
  for ( PAIR_ITER p = pair_begin; p != pair_end; ++p )
    std::cout << *p->first << " " << *p->second << std::endl;

  std::cout << "calling sort..." << std::endl;
  std::sort( pair_begin , pair_end , &proxy_comp<std::vector<int>::iterator , std::vector<double>::iterator> );

  std::cout << "paired arrays now:" << std::endl;
  for ( PAIR_ITER p = pair_begin; p != pair_end; ++p )
    std::cout << *p->first << " " << *p->second << std::endl;




  //   
  // EXAMPLE 2: TRIPLET (PAIR OF PAIR)
  //

  typedef piterator< std::vector<double>::iterator , std::vector<double>::iterator > DOUBLET_ITER;
  typedef piterator< std::vector<int>::iterator , DOUBLET_ITER > TRIPLET_ITER;


  TRIPLET_ITER triplet_begin( iv.begin(), DOUBLET_ITER( dv1.begin() , dv2.begin() ) );
  TRIPLET_ITER triplet_end(   iv.end(),   DOUBLET_ITER( dv1.end() , dv2.end() ) );


  std::cout << "tripleted arrays now:" << std::endl;
  for ( TRIPLET_ITER p = triplet_begin; p != triplet_end; ++p )
    std::cout << *p->first << " " 
          << *p->second->first << " " 
          << *p->second->second << std::endl;


  std::cout << "iter_swap 1st and second elements..." << std::endl;
  std::iter_swap( triplet_begin , triplet_begin+1 );


  std::cout << "tripleted arrays now:" << std::endl;
  for ( TRIPLET_ITER p = triplet_begin; p != triplet_end; ++p )
    std::cout << *p->first << " " 
          << *p->second->first << " " 
          << *p->second->second << std::endl;


  std::cout << "calling sort..." << std::endl;
  std::sort( triplet_begin, triplet_end, &proxy_comp< std::vector<int>::iterator , piterator< std::vector<double>::iterator , std::vector<double>::iterator > > );


  std::cout << "tripleted arrays now:" << std::endl;
  for ( TRIPLET_ITER p = triplet_begin; p != triplet_end; ++p )
    std::cout << *p->first << " " 
          << *p->second->first << " " 
          << *p->second->second << std::endl;


  return 0;
}

Вот вывод вышеуказанной программы

paired arrays now:
10 -0.5
20 -1.5
3 -2.5
swap 1st and 3rd elements...
paired arrays now:
3 -2.5
20 -1.5
10 -0.5
calling sort...
paired arrays now:
3 -2.5
10 -0.5
20 -1.5
tripleted arrays now:
3 -2.5 10.5
10 -0.5 11.5
20 -1.5 12.5
iter_swap 1st and second elements...
tripleted arrays now:
10 -0.5 11.5
3 -2.5 10.5
20 -1.5 12.5
calling sort...
tripleted arrays now:
3 -2.5 10.5
10 -0.5 11.5
20 -1.5 12.5
2 голосов
/ 01 июля 2011

Прежде всего, вы должны понимать, что std::nth_element уже выполняет разделение, которое вы описываете. Он находит элемент N th точно так, как вы ожидаете, но он также разбивает данные на две части - все элементы, меньшие, чем найденный элемент, будут находиться в нижних местах коллекция и все более крупные элементы справа от нее.

Лично я думаю, что я бы поступил немного иначе: если вам все еще нужен исходный порядок данных, но он также должен быть отсортирован в каком-то другом порядке, создайте отсортированный индекс и оставьте исходные данные в покое. Учитывая, что ваши исходные данные (очевидно) находятся в std::vector, я бы лично поместил подписки в индекс (добавление новых элементов в конец вектора не сделает их недействительными, как это будут итераторы).

std::vector<int> index(data.size());

template <class T>
struct cmp { 
   T const &data;
public:
   cmp(T const &array) : data(array) {}

   bool operator()(int a, int b) { return data[a] < data[b]; }
};

for (int i=0; i<index.size(); i++)
    index[i] = i;

std::sort(index.begin(), index.end(), cmp(your_data));

Затем, чтобы использовать данные в исходном порядке, вы просто индексируете исходный массив / вектор, как your_data[i]. Чтобы использовать данные в отсортированном порядке, вы должны использовать что-то вроде your_data[index[i]].

Конечно, вы можете встроить все это в индексный класс, так что вы просто используете индексный класс 'operator[] для фактического индексирования исходных данных в отсортированном порядке. Класс cmp выше уже показывает, как сделать большую часть этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...